Schema Search
Pesquisa de esquema em linguagem natural na memória sobre esquemas de banco de dados.
Documentação
Schema Search
Nota: O projeto foi movido para um novo repositório e está sendo mantido pela SignalPilot Labs. O código no repositório atual está disponível apenas para referência.
Um Servidor MCP para Busca em Linguagem Natural sobre Esquemas de RDBMS. Encontre as tabelas exatas que você precisa, com todos os seus relacionamentos mapeados, em milissegundos. Nenhuma configuração de banco de dados vetorial é necessária.
Por quê
Você tem 200 tabelas no seu banco de dados. Alguém pergunta "onde estão armazenados os reembolsos dos usuários?"
Você poderia:
- Procurar em arquivos SQL por 20 minutos
- Passar o esquema completo para um LLM e vê-lo ter dificuldades com 200 tabelas
Ou construir embeddings esquemáticos das suas tabelas, armazená-los em memória e consultar em linguagem natural em um servidor MCP.
Benefícios
- Nenhuma configuração de banco de dados vetorial é necessária
- Pegada de memória pequena -- escala facilmente para 1000 tabelas e mais de 10.000 colunas.
- Latência de consulta em milissegundos
Instalação
Rápido por padrão - A instalação base usa apenas busca BM25/difusa (sem PyTorch):
# Minimal install (BM25 + fuzzy only, ~10MB)
pip install "schema-search[postgres]"
# With semantic/hybrid search support (~500MB with PyTorch)
pip install "schema-search[postgres,semantic]"
# With LLM chunking
pip install "schema-search[postgres,semantic,llm]"
# With MCP server
pip install "schema-search[postgres,semantic,mcp]"
# Other databases
pip install "schema-search[mysql,semantic]" # MySQL
pip install "schema-search[snowflake,semantic]" # Snowflake
pip install "schema-search[bigquery,semantic]" # BigQuery
pip install "schema-search[databricks,semantic]" # Databricks
Extras:
[semantic]: Habilita busca semântica/híbrida e reordenação CrossEncoder (adiciona sentence-transformers)[llm]: Habilita divisão de esquema baseada em LLM (adiciona openai)[mcp]: Suporte a servidor MCP (adiciona fastmcp)
Configuração
Edite config.yml:
logging:
level: "WARNING"
embedding:
location: "memory" # Options: "memory", "vectordb" (coming soon)
model: "multi-qa-MiniLM-L6-cos-v1"
metric: "cosine" # Options: "cosine", "euclidean", "manhattan", "dot"
batch_size: 32
show_progress: false
cache_dir: "/tmp/.schema_search_cache"
chunking:
strategy: "raw" # Options: "raw", "llm"
max_tokens: 256
overlap_tokens: 50
model: "gpt-4o-mini"
search:
# Search strategy: "semantic" (embeddings), "bm25" (BM25 lexical), "fuzzy" (fuzzy string matching), "hybrid" (semantic + bm25)
strategy: "bm25"
initial_top_k: 20
rerank_top_k: 5
semantic_weight: 0.67 # For hybrid search (bm25_weight = 1 - semantic_weight)
hops: 1 # Number of foreign key hops for graph expansion (0-2 recommended)
reranker:
# CrossEncoder model for reranking. Set to null to disable reranking
model: null # "Alibaba-NLP/gte-reranker-modernbert-base"
schema:
include_columns: true
include_indices: true
include_foreign_keys: true
include_constraints: true
output:
format: "markdown" # Options: "json", "markdown"
limit: 5 # Default number of results to return
Servidor MCP
Integre com o Claude Desktop ou qualquer cliente MCP.
Configuração
Adicione à sua configuração MCP (ex.: ~/.cursor/mcp.json ou configuração do Claude Desktop):
Usando uv (Recomendado):
{
"mcpServers": {
"schema-search": {
"command": "uvx",
"args": [
"schema-search[postgres,mcp]",
"postgresql://user:pass@localhost/db",
"optional/path/to/config.yml",
"optional llm_api_key",
"optional llm_base_url"
]
}
}
}
Usando pip:
{
"mcpServers": {
"schema-search": {
// conda: /Users/<username>/opt/miniconda3/envs/<your env>/bin/schema-search",
"command": "path/to/schema-search",
"args": [
"postgresql://user:pass@localhost/db",
"optional/path/to/config.yml",
"optional llm_api_key",
"optional llm_base_url"
]
}
}
}
A chave da API LLM e a URL base são necessárias apenas se você usar resumos de esquema gerados por LLM (config.chunking.strategy = 'llm').
Uso via CLI
schema-search "postgresql://user:pass@localhost/db" "optional/path/to/config.yml"
Argumentos opcionais: [config_path] [llm_api_key] [llm_base_url]
O servidor expõe schema_search(query, hops, limit) para consultas de esquema em linguagem natural.
Uso em Python
from sqlalchemy import create_engine
from schema_search import SchemaSearch
# PostgreSQL
engine = create_engine("postgresql://user:pass@localhost/db")
sc = SchemaSearch(
engine=engine,
config_path="optional/path/to/config.yml", # default: config.yml
llm_api_key="optional llm api key",
llm_base_url="optional llm base url"
)
sc.index(force=False) # default is False
results = sc.search("where are user refunds stored?")
# Default output is markdown - render with str()
print(results) # Formatted markdown with schemas, relationships, and scores
# Access underlying data as dictionary
result_dict = results.to_dict()
for result in result_dict['results']:
print(result['table']) # "refund_transactions"
print(result['schema']) # Full column info, types, constraints
print(result['related_tables']) # ["users", "payments", "transactions"]
# Override output format explicitly
json_results = sc.search("where are user refunds stored?", output_format="json")
print(json_results) # JSON formatted string
# Override hops, limit, search strategy, and output format
results = sc.search("user_table", hops=1, limit=5, search_type="hybrid", output_format="markdown")
sc.index() detecta automaticamente mudanças no esquema e atualiza os metadados em cache, então raramente você precisará forçar um reindexação manualmente.
Strings de Conexão com Banco de Dados
O Schema Search usa strings de conexão SQLAlchemy:
# PostgreSQL
engine = create_engine("postgresql://postgres:mypass@localhost:5432/mydb")
# MySQL
engine = create_engine("mysql+pymysql://root:mypass@localhost:3306/mydb")
# Snowflake
engine = create_engine("snowflake://myuser:mypass@xy12345.us-east-1/MYDB/PUBLIC?warehouse=COMPUTE_WH&role=ANALYST")
# BigQuery
engine = create_engine("bigquery://my-project/my-dataset")
# Databricks
token = "dapi..."
host = "dbc-xyz.cloud.databricks.com"
http_path = "/sql/1.0/warehouses/abc123"
catalog = "main"
schema = "default" # Optional
# Without schema (queries across all schemas in catalog)
engine = create_engine(
f"databricks://token:{token}@{host}?http_path={http_path}&catalog={catalog}",
connect_args={"user_agent_entry": "schema-search"}
)
# With schema (limits to specific schema)
engine = create_engine(
f"databricks://token:{token}@{host}?http_path={http_path}&catalog={catalog}&schema={schema}",
connect_args={"user_agent_entry": "schema-search"}
)
Estratégias de Busca
O Schema Search suporta quatro estratégias de busca:
- bm25: Busca lexical usando o algoritmo de ranqueamento BM25 (sem dependências de ML)
- fuzzy: Correspondência de strings em nomes de tabelas/colunas usando correspondência difusa (sem dependências de ML)
- semantic: Busca por similaridade baseada em embeddings usando sentence transformers (requer
[semantic]) - hybrid: Combina pontuações semânticas e bm25 (padrão: 67% semântico, 33% bm25) (requer
[semantic])
Cada estratégia realiza seu próprio ranqueamento inicial e, opcionalmente, aplica reordenação CrossEncoder se reranker.model estiver configurado (requer [semantic]). Defina reranker.model como null para desabilitar a reordenação.
Comparação de Desempenho
Nós fizemos benchmark no conjunto de dados Spider (1.234 consultas de treino em 18 bancos de dados) usando o config.yml padrão.
Memória: O modelo de embeddings requer ~90 MB e o reordenador opcional adiciona ~155 MB. A memória real do processo depende do seu runtime Python.
Sem Reordenador (reranker.model: null)

- Indexação: 0,22s ± 0,08s por banco de dados (18 no total).
- Precisão: Híbrido lidera com Recall@1 62% / MRR 0,93; Semântico segue com Recall@1 58% / MRR 0,89.
- Latência: BM25 e Fuzzy retornam em ~5ms; Semântico gasta ~15ms; Híbrido (semântico + fuzzy) tem média de 52ms.
- Linha de base Fuzzy: Recall@1 22%, destacando a necessidade de sinais semânticos em consultas em linguagem natural.
Com Reordenador (Alibaba-NLP/gte-reranker-modernbert-base)

- Indexação: 0,25s ± 0,05s por banco de dados (mesmos 18 DBs).
- Precisão: Todas as estratégias convergem em torno de Recall@1 62% e MRR ≈ 0,92; Fuzzy salta de 51% → 92% MRR.
- Compensação de latência: A passada extra do CrossEncoder eleva a latência por consulta para ~0,18–0,29s dependendo da estratégia.
- Recomendação: Habilite o reordenador quando a precisão for mais importante; desabilite-o para buscas de latência ultrabaixa.
Você pode sobrescrever a estratégia de busca, saltos e limite no momento da consulta:
# Use fuzzy search instead of default
results = sc.search("user_table", search_type="fuzzy")
# Use BM25 for keyword-based search
results = sc.search("transactions payments", search_type="bm25")
# Use hybrid for best of both worlds
results = sc.search("where are user refunds?", search_type="hybrid")
# Override hops and limit
results = sc.search("user refunds", hops=2, limit=10) # Expand 2 hops, return 10 tables
# Disable graph expansion
results = sc.search("user_table", hops=0) # Only direct matches, no foreign key traversal
Formatos de Saída
O Schema Search retorna um objeto SearchResult que pode ser renderizado em vários formatos:
- markdown (padrão): Markdown formatado com esquemas de tabela hierárquicos
- json: Saída JSON estruturada
O objeto SearchResult tem:
- Método
__str__(): Renderiza usando o formato configurado (markdown ou json) - Método
.to_dict(): Retorna dicionário bruto para acesso programático
Configure o formato padrão em config.yml:
output:
format: "markdown" # or "json"
limit: 5 # Default number of results
Sobrescreva no momento da consulta:
# Default markdown output - just print the object
results = sc.search("user payments")
print(results) # Formatted markdown
# Access underlying data as dictionary
data = results.to_dict()
print(data['results'][0]['table']) # "users"
# Override to JSON format
json_results = sc.search("user payments", output_format="json")
print(json_results) # JSON formatted string
A saída Markdown inclui:
- Nome da tabela e pontuação de relevância
- Chaves primárias e colunas com tipos/restrições
- Relacionamentos de chaves estrangeiras
- Índices e restrições
- Tabelas relacionadas da expansão do grafo
- Trechos de conteúdo correspondentes
Divisão com LLM
Use LLM para gerar resumos semânticos em vez de texto de esquema bruto (requer o extra [llm]):
- Instale:
pip install "schema-search[postgres,llm]" - Defina
strategy: "llm"emconfig.yml - Passe as credenciais da API:
sc = SchemaSearch(
engine,
llm_api_key="sk-...",
llm_base_url="https://api.openai.com/v1/" # optional
)
Como Funciona
- Extrai esquemas do banco de dados usando o inspetor SQLAlchemy
- Divide esquemas em partes digeríveis (markdown ou resumos gerados por LLM)
- Busca inicial usando a estratégia selecionada (semântico/BM25/difuso)
- Expande via chaves estrangeiras para encontrar tabelas relacionadas (saltos configuráveis)
- Reordenação opcional com CrossEncoder para refinar resultados
- Retorna as principais tabelas com esquema completo e relacionamentos
Cache armazenado em /tmp/.schema_search_cache/ (configurável em config.yml)
Licença
MIT