Skip to main content
Glama
jg-cordeiro

recife-open-data-mcp

by jg-cordeiro
README.md
# Recife Open Data MCP

Servidor [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) para consultar dados publicos abertos do Recife em linguagem natural. O servidor expoe ferramentas que permitem a qualquer cliente MCP explorar o schema de um banco DuckDB local e executar consultas SQL read-only sobre os datasets do municipio.

## Arquitetura

O projeto tem tres camadas independentes:

```
┌──────────────────────────────────────────────────────┐
│                  Clientes MCP                        │
│  (Claude Desktop, Cursor, qualquer cliente MCP)      │
└──────────────┬───────────────────────────────────────┘
               │ protocolo MCP (stdio ou HTTP/SSE)
┌──────────────▼───────────────────────────────────────┐
│            Servidor MCP (FastMCP)                     │
│                                                      │
│  Ferramentas:                                        │
│   list_tables · describe_table · search_schema       │
│   list_databases · create_sql · execute_sql          │
│                                                      │
│  Resources:                                          │
│   dicionarios de dados (JSON)                        │
│                                                      │
│  Guardrails:                                         │
│   somente SELECT/WITH/EXPLAIN · bloqueio de DDL/DML  │
└──────────────┬───────────────────────────────────────┘
               │
┌──────────────▼───────────────────────────────────────┐
│             DuckDB (banco embutido)                   │
│  ./data/recife.duckdb                                │
│                                                      │
│  Tabelas:                                            │
│   situacao_final_dos_alunos_por_periodo_letivo       │
│   registro_das_infracoes_de_transito_-_cttu          │
│   naufragios_do_recife                               │
└──────────────────────────────────────────────────────┘
```

### Servidor MCP (`server/`)

O componente principal. Um servidor FastMCP que expoe ferramentas para explorar o schema do banco e consultar dados. Pode rodar via **stdio** (protocolo MCP nativo) ou **HTTP/SSE** (FastAPI). Toda consulta SQL e validada como read-only antes da execucao.

A geracao de SQL a partir de linguagem natural e feita pelo modelo **Gemini 2.5 Flash** via OpenRouter, com um prompt que forca a exploracao previa do schema e aplica regras de seguranca.

| Arquivo | Responsabilidade |
|---|---|
| `server/main.py` | Aplicacao FastMCP com ferramentas e resources |
| `server/http_server.py` | Transporte HTTP/SSE via FastAPI |
| `server/db.py` | Interface com o DuckDB (queries, introspeccao de schema) |
| `server/openrouter_client.py` | Cliente OpenRouter para geracao de SQL via Gemini 2.5 Flash |
| `server/sql_guard.py` | Validacao de SQL (somente leitura, statement unico) |
| `server/config.py` | Configuracao via variaveis de ambiente |

### Cliente CLI (`client.py`)

Um agente interativo para testes que simula o fluxo de tool calling localmente:

1. Envia a pergunta do usuario ao Gemini 2.5 Flash via OpenRouter
2. O modelo responde com chamadas de ferramentas (`tool_calls`)
3. O cliente executa cada ferramenta chamando diretamente as funcoes Python do servidor (e.g. `server.db.list_tables()`, `server.openrouter_client.generate_sql()`)
4. Os resultados sao enviados de volta ao modelo
5. O ciclo repete ate o modelo produzir uma resposta final (maximo 10 iteracoes)

Isso permite testar o fluxo completo de tool calling sem precisar de um cliente MCP externo.

### Scripts

| Arquivo | Responsabilidade |
|---|---|
| `scripts/ingest.py` | Carga de CSVs no DuckDB a partir de descritores JSON |
| `scripts/run_eval.py` | Execucao de casos de avaliacao comparando SQL gerado com queries de referencia |

### Datasets (`datasets/`)

CSVs originais obtidos do [Portal de Dados Abertos do Recife](http://dados.recife.pe.gov.br/), rastreados via Git LFS:

| Dataset | Diretorio | Periodo |
|---|---|---|
| Situacao final dos alunos por periodo letivo | `datasets/situacao-final-estudantes/` | 2012-2024 |
| Registro das infracoes de transito (CTTU) | `datasets/registro-das-infracoes-de-transito/` | 2010-2024 |
| Naufragios do Recife | `datasets/naufragios-recife/` | -- |


### Avaliacao (`eval_cases.json`)

30 casos de teste (10 por dataset) que comparam o SQL gerado com queries de referencia. Tipos de comparacao: numerica, ranking e lista.

## Pre-requisitos

- Python 3.12+
- [Git LFS](https://git-lfs.com/) (`brew install git-lfs && git lfs install`)
- Chave do [OpenRouter](https://openrouter.ai/) com acesso ao modelo `google/gemini-2.5-flash`

## Configuracao rapida

```bash
# 1. Clone e baixe arquivos LFS
git clone https://github.com/<usuario>/recife-open-data-mcp.git
cd recife-open-data-mcp
git lfs pull

# 2. Crie o ambiente e instale dependencias
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# 3. Configure variaveis de ambiente
cp .env.example .env
# Edite .env e preencha OPENROUTER_API_KEY
```

## Executando

### Servidor MCP (stdio)

```bash
python -m server.main
```

Use esse modo para conectar clientes MCP como Claude Desktop ou Cursor.

### Servidor HTTP

```bash
uvicorn server.http_server:app --reload --port 8000
```

Endpoints disponiveis em `http://localhost:8000/mcp/v1/`.

### Cliente CLI interativo

```bash
python client.py interactive
```

Comandos internos:
- `:tools` -- lista ferramentas disponiveis
- `:tables` -- lista tabelas do banco
- `:schemas` -- lista schemas
- `:describe <tabela>` -- descreve colunas de uma tabela
- `:search <termo>` -- busca tabelas/colunas por termo

## Ferramentas MCP
O servidor expoe ferramentas voltadas a duas necessidades: (i) exploracao incremental do schema e (ii) consulta de dados. A exploracao incremental evita enviar o schema completo na janela de contexto e prepara o prototipo para cenarios com multiplos bancos.

| Ferramenta | Descricao |
|---|---|
| `list_tables()` | Lista todas as tabelas com schema |
| `describe_table(table_name)` | Retorna colunas, tipos e nullability de uma tabela |
| `search_schema(search_term)` | Busca tabelas/colunas por termo |
| `list_databases()` | Lista schemas disponiveis |
| `create_sql(question, schema_context?)` | Gera SQL read-only a partir de linguagem natural via Gemini 2.5 Flash |
| `execute_sql(sql)` | Executa SQL pre-validado como somente leitura |

O racional completo das ferramentas e do system prompt esta em [`FERRAMENTAS_MCP.md`](FERRAMENTAS_MCP.md).

### Guardrails de SQL

- Somente `SELECT`, `WITH` e `EXPLAIN` sao permitidos
- Multiplos statements em uma unica query sao bloqueados
- DDL/DML (`INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `TRUNCATE`, `CREATE`, `GRANT`, `REVOKE`) sao bloqueados

### System prompt

O prompt enviado ao Gemini 2.5 Flash (em `server/openrouter_client.py`) inclui instrucoes para:

- **Forcar exploracao do schema**: o modelo deve chamar `list_tables` e `describe_table` antes de gerar qualquer SQL, evitando colunas inventadas
- **Consultar dicionarios de dados**: via MCP resources, para entender o significado dos campos
- **Usar somente nomes retornados pelas ferramentas**: reforcar vinculo com o schema real
- **Quoting obrigatorio**: nomes com acentos, hifens e espacos devem ser sempre entre aspas duplas (`"schema"."tabela"."coluna"`)
- **Regras de filtragem**: usar `LIKE`/regex para filtros categoricos, `substr`/`EXTRACT` para datas ISO, `regexp_extract` para datas em texto livre
- **Exemplos few-shot**: queries reais dos tres datasets para guiar formato e aliases

## Dados e ingestao

Os CSVs sao mantidos como vieram do portal. A carga no DuckDB e feita por descritores JSON que definem tabela e schema. Detalhes em [`INGESTAO_DATASETS.md`](INGESTAO_DATASETS.md).

```bash
# Ingestao em lote
python -m scripts.ingest batch --input-dir datasets/situacao-final-estudantes
python -m scripts.ingest batch --input-dir datasets/registro-das-infracoes-de-transito
python -m scripts.ingest batch --input-dir datasets/naufragios-recife
```

## Avaliacao

```bash
python -m scripts.run_eval --help
python -m scripts.run_eval
```

Executa os 30 casos de `eval_cases.json` e gera um relatorio Markdown em `eval_runs/`.

Cada caso:

1. Envia a pergunta ao modelo com as ferramentas disponiveis
2. O modelo explora o schema e gera SQL via tool calling
3. Executa o SQL gerado e o SQL de referencia
4. Compara os resultados (numerico, ranking ou lista)
## Variaveis de ambiente

| Variavel | Padrao | Descricao |
|---|---|---|
| `OPENROUTER_API_KEY` | (obrigatorio) | Chave de API do OpenRouter |
| `OPENROUTER_MODEL` | `google/gemini-2.5-flash` | Modelo LLM usado para geracao de SQL |
| `DUCKDB_DATA_DIR` | `./data` | Diretorio do banco DuckDB |
| `MAX_RESULT_ROWS` | `200` | Limite de linhas retornadas |
| `STATEMENT_TIMEOUT_MS` | `10000` | Timeout de execucao SQL (ms) |
| `HTTP_PORT` | `8000` | Porta do servidor HTTP |

## Documentacao complementar

Este projeto e o resultado de um **Trabalho de Conclusao de Curso (TCC)** do bacharelado em **Sistemas de Informacao** na **Universidade Federal de Pernambuco (UFPE)**.

### Resumo

Os dados abertos governamentais tem grande valor social, mas muitos cidadaos enfrentam barreiras tecnicas para acessa-los e explora-los. Este trabalho investiga a viabilidade tecnica de uma interface de consulta em linguagem natural para os dados publicos do Recife. Desenvolveu-se um prototipo funcional utilizando o protocolo Model Context Protocol (MCP) como camada de abstracao entre uma base de dados relacional local e um modelo de linguagem, responsavel por gerar consultas SQL a partir de perguntas em portugues. Realizou-se uma avaliacao exploratoria do prototipo com um conjunto de perguntas em linguagem natural aplicadas a tres conjuntos de dados reais do Recife (educacao, transito e naufragios). Os resultados indicam bom desempenho em consultas simples, enquanto consultas mais complexas revelaram desafios devido a qualidade dos dados e as variacoes de formato. Conclui-se que a solucao proposta e promissora para ampliar o acesso exploratorio de usuarios nao especialistas a dados publicos, embora sua eficacia dependa diretamente da qualidade e padronizacao das bases de dados.


## Contribuindo

Veja [`CONTRIBUTING.md`](CONTRIBUTING.md).