Skip to main content
Glama
wganalytics

mcp-crewai-giulia-ai

by wganalytics

PRJ-07 — CrewAI + MCP: Text-to-SQL

Sistema multi-agente CrewAI + servidor MCP que faz text-to-SQL: o usuário conversa (Streamlit), um agente CrewAI usa as tools MCP, o servidor gera SQL real a partir de linguagem natural (com base no schema) e consulta bancos PostgreSQL de demonstração (ecommerce e clinica). Corresponde ao Capítulo 8 do livro Model Context Protocol (Sandeco).

Os mocks originais (SQL fixo, MockCursor, URI falsa) foram substituídos por: geração de SQL por um Crew real (crew_ai_query.py), conexão psycopg2 real (postgres_connection.py) e URI a partir de env (postgres_databases.py). Consultas são somente-leitura — ver Segurança.

Multi-provider

Modelo por LLM_MODEL (OpenAI/Anthropic/Gemini/OpenRouter) — ver .env.example.

Related MCP server: mcp-db-server

Pré-requisitos: PostgreSQL com massa de teste

# cria os 2 bancos e popula com os dados demo (ajuste user/host conforme seu .env)
createdb ecommerce && createdb clinica
psql -d ecommerce -f data/seed_ecommerce.sql
psql -d clinica   -f data/seed_clinica.sql

Schemas descritos em src/schema_ecommerce.yaml e src/schema_clinica.yaml (o agente usa o YAML para gerar SQL correto).

Uso

uv sync
cp .env.example .env      # defina LLM_MODEL + chave do provider + credenciais PG
uv run streamlit run src/main.py

Fluxo: pergunta em linguagem natural → agente CrewAI chama a tool MCP buscar_dados_sql → Crew gera o SELECT a partir do schema → validação → executa no Postgres em sessão read-only → devolve os dados. Ex.: "quais os 3 produtos mais vendidos no ecommerce?"

Segurança

O SQL é gerado por um LLM a partir de texto do usuário, então o vetor a considerar é prompt injection. A defesa tem duas camadas, e só a segunda é garantia.

Camada 1 — validação na aplicação (src/sql_guard.py)

Falha cedo, com motivo legível, antes de gastar uma ida ao banco:

  • Um único comando. Múltiplos statements são rejeitados — psycopg2 executa vários comandos separados por ; numa única chamada de execute().

  • Allowlist de início: SELECT, WITH, VALUES ou TABLE.

  • Denylist complementar para CTE que escreve (WITH x AS (DELETE ... RETURNING *), recurso real do PostgreSQL).

  • Comentários (--, /* */ aninhados), literais ('...', $$...$$) e identificadores ("...") são analisados corretamente: uma palavra reservada dentro de um dado não vira comando, e um ; dentro de um literal não separa statement.

Versão anterior: a validação era sql.lower().lstrip().startswith("select"). SELECT 1; DROP TABLE clientes começa com select — e passava. Validar prefixo é validar string; o necessário é validar comando. Coberto por tests/test_sql_guard.py::test_rejeita_o_bypass_original.

Camada 2 — garantia no banco (src/postgres_connection.py)

A sessão é aberta com set_session(readonly=True). Quem recusa a escrita é o próprio PostgreSQL (ReadOnlySqlTransaction), não código Python. Há também um statement_timeout (PG_STATEMENT_TIMEOUT_MS, padrão 15s) para que uma query gerada por LLM não prenda uma conexão indefinidamente.

Recomendado: aponte PG_USER para um role sem permissão de escrita — defesa em profundidade, independente da aplicação.

CREATE ROLE giulia_ro LOGIN PASSWORD 'troque-isto';
GRANT CONNECT ON DATABASE ecommerce TO giulia_ro;
GRANT USAGE ON SCHEMA public TO giulia_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO giulia_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO giulia_ro;

Testes

uv run pytest                          # 58 testes do sql_guard (lógica pura, sem banco)

Os testes da camada 2 exigem um PostgreSQL de verdade — é o único jeito de provar que quem recusa a escrita é o banco. Sem PRJ07_TEST_DSN definido eles são pulados:

docker run -d --name prj07-mcp-pg -e POSTGRES_PASSWORD=postgres -p 55432:5432 postgres:16-alpine
# crie os bancos e rode os seeds (ver "Pré-requisitos" acima)

export PRJ07_TEST_DSN="postgresql://postgres:postgres@localhost:55432/ecommerce"
uv run pytest                          # 73 testes (58 + 15 de integração)

Entre os testes de integração está test_bypass_do_guard_ainda_seria_barrado_pelo_banco: mesmo que a camada 1 falhasse, SELECT 1; DROP TABLE clientes levanta ReadOnlySqlTransaction. É a definição de defesa em profundidade.

Available Tools

2 tools
buscar_dados_sqlA

Responde uma pergunta em linguagem natural consultando o banco indicado.

O SQL é gerado a partir do schema, validado como somente-leitura e executado numa sessão read-only. Use listar_databases para descobrir os valores de database_name.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes
database_nameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does substantial work: it discloses that SQL is generated from the schema, validated as read-only, and executed in a read-only session, giving the agent a clear safety profile for a query tool. It omits auth/permission requirements, rate limits, and error behavior, so it is strong but not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the core purpose followed by the read-only guarantee and the sibling pointer. No filler text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, and the description covers purpose, safety guarantees, and parameter discovery. What remains missing is any note on permissions, limits, or failure modes for a tool that runs generated SQL, keeping it just under fully complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It clarifies that 'query' is a natural-language question rather than raw SQL, which is valuable, and that 'database_name' is a value discoverable via listar_databases. It does not specify the expected format or naming convention for database_name, leaving a real gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: it answers a natural-language question by querying a given database. It also implicitly separates itself from the sibling by telling the agent to use listar_databases for database_name values, so the boundary between the two tools is clear without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit routing guidance for the database_name parameter ('Use listar_databases para descobrir os valores de database_name'), which is the main selection decision here. It does not, however, state when this tool is inappropriate or describe exclusions, so it falls short of a full 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

listar_databasesA

Lista os bancos disponíveis para consulta.

Use antes de buscar_dados_sql para descobrir o valor válido de database_name.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full behavioral burden. "Lista" implies a safe read, and the workflow role is clear, but it says nothing about permissions, scope of visibility, or cost/latency. With an output schema present, return-shape disclosure is not required, so a 3 is fair.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, zero waste, with the purpose front-loaded and the workflow instruction following. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter enumeration tool with an output schema covering the return values, the description supplies everything needed: what it lists and how to use the result. No gaps remain for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. The description additionally names `database_name`, a parameter of the sibling tool, which usefully links the output of this call to the input of the next.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Lista os bancos disponíveis para consulta") and immediately scopes it relative to the sibling tool. An agent can tell exactly what this returns and how it differs from buscar_dados_sql without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly prescribes the ordering: call this before buscar_dados_sql to discover the valid value of database_name. This is a concrete when-to-use directive tied to a named alternative, leaving nothing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.0
    • First observedbuscar_dados_sql
    • First observedlistar_databases

TDQS

A4.2/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: one discovers available databases, the other answers natural-language questions via SQL. The cross-references in each description make the intended order of use explicit, leaving no realistic chance of misselection.

Naming Consistency4/5

Both names follow a verb_noun snake_case pattern in Portuguese (buscar_dados_sql, listar_databases), which is readable and predictable. The minor deviation is the '_sql' suffix on one tool that isn't mirrored in the other.

Tool Count3/5

Two tools is borderline thin for a server, though for a strictly read-only NL-to-SQL workflow it is defensible. A schema-inspection or query-explanation tool would round out the set without bloat.

Completeness4/5

Discovery plus read-only querying covers the core stated purpose with no obvious dead ends. A dedicated schema/table introspection tool is a minor gap, since schema knowledge is currently only implicit inside buscar_dados_sql.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    An MCP server that gives AI agents direct read-only access to PostgreSQL databases, enabling natural language analytics through tools for schema exploration, querying, trend analysis, and data quality checks.
    11
    5
    MIT
  • F
    license
    Not graded
    quality
    A
    maintenance
    An MCP server that exposes relational databases (PostgreSQL/MySQL) to AI agents with natural language to SQL query support.
    19
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Read-only Text-to-SQL MCP server for PostgreSQL and MySQL that lets users query databases using natural language, with robust multi-layer safety guarantees against writes.
    27 npm
    MIT