Skip to main content
Glama
pedroct

FastAPI Knowledge Base MCP Server

by pedroct
README.md
# fastapi-llm-toolkit

Toolkit de apoio à LLM no desenvolvimento com FastAPI. Monorepo com três
consumidores sobre um núcleo compartilhado: **RAG**, **MCP** e **Skills**.

## Por que monorepo

Os três componentes compartilham o mesmo domínio — o conhecimento da doc
`/reference/*` do FastAPI. O chunker, os modelos e o catálogo de fontes vivem
uma única vez em `core`; uma mudança de versão do FastAPI se propaga num só
lugar. A regra que evita o monorepo virar uma bola acoplada:

> Tudo que é compartilhado vive em `core`. Os pacotes dependem de `core`,
> **nunca um do outro** (exceto MCP→RAG, que é consumo de interface).

Quando o MCP amadurecer como produto instalável por terceiros, as fronteiras
já estão desenhadas — extrair para repo próprio é mecânico.

## Estrutura

```
fastapi-llm-toolkit/
├── packages/
│   ├── core/         fastapi_kb_core  — modelos, chunker, catálogo de URLs (sem deps)
│   ├── rag/          fastapi_kb_rag   — coleta, ingestão, índice/recuperação (dep: core)
│   ├── mcp-server/   fastapi_kb_mcp   — servidor MCP (dep: core, rag)
│   └── skills/       SKILL.md por skill (endpoint-scaffold, dependency-injection, ...)
├── docs/raw/         1 .md por página coletada (1ª linha = URL canônica)
└── output/           chunks.jsonl gerado pela ingestão
```

### Grafo de dependência

```
core  <-  rag  <-  mcp-server
  ^________________/
skills: artefatos estáticos (SKILL.md); consomem o MCP/RAG em runtime
```

## Setup

```bash
uv sync                                                    # instala workspace + grupo dev
uv run pre-commit install --hook-type commit-msg --hook-type pre-commit
```

## Fluxo do RAG

```bash
# 1. coletar as 21+ páginas -> docs/raw/ (standalone, reprodutível)
python3 -m fastapi_kb_rag.collect --out docs/raw

# 2. ingerir -> output/chunks.jsonl
python3 -m fastapi_kb_rag.ingest --from-dir docs/raw --version 0.115.x

# 3. indexar no Qdrant com embeddings locais (sentence-transformers)
#    embarcado (sem Docker, persiste em .qdrant/):
python3 -m fastapi_kb_rag.build_index --chunks output/chunks.jsonl --path .qdrant --recreate
#    ou via Docker (produção):
#    docker run -p 6333:6333 -v $(pwd)/qdrant_storage:/qdrant/storage qdrant/qdrant
python3 -m fastapi_kb_rag.build_index --chunks output/chunks.jsonl --url http://localhost:6333 --recreate

# consulta de sanidade
python3 -m fastapi_kb_rag.build_index --query "how to add a GET route" --path .qdrant
```

### Stack do índice

- **Vector store:** Qdrant (filtros de payload de 1ª classe). Embarcado p/ dev,
  servidor Docker p/ produção — mesma classe `QdrantIndex`.
- **Embeddings:** locais via `sentence-transformers`, modelo
  `BAAI/bge-small-en-v1.5` (384 dims), sem custo de API. O embedder é injetável
  (`Embedder` protocol em `embedder.py`), então trocar por OpenAI/Voyage depois
  não toca o índice.
- **Filtros no retrieval:** `version` (sempre), `symbol`, `kind`, e
  `include_low_priority` (default False → exclui chunks `source_code`).

### Pipeline de chunking (4 estágios)

A ingestão aplica, nesta ordem:
1. **chunk_reference_page** — quebra a página por símbolo/membro (mkdocstrings).
2. **split_source_code** — isola o bloco "Source code in..." (implementação
   interna do FastAPI) num chunk `source_code` de `priority=low`. Era a maior
   fonte de ruído: ~90% do tamanho dos métodos grandes.
3. **coalesce_small_members** — agrupa membros minúsculos (< 40 tok) do mesmo
   símbolo num `members_group`.
4. **split_large_param_chunks** — divide tabelas de parâmetros de métodos
   grandes em `param_groups` de ~4 params, cada um com a assinatura do método
   pai como contexto.

Resultado típico (FastAPI 0.115.x): ~870 chunks, dos quais ~680 "normais"
(mediana 70 tok, máx 1480) e ~190 `source_code` de baixa prioridade.

### Tipos de chunk

| kind            | o que é                                   | priority |
|-----------------|-------------------------------------------|----------|
| `page_intro`    | introdução da página (+ exemplos de página) | normal |
| `symbol`        | classe/função (assinatura + descrição)    | normal   |
| `member`        | atributo/método                            | normal   |
| `members_group` | vários membros pequenos agrupados          | normal   |
| `param_group`   | ~4 parâmetros de um método + assinatura    | normal   |
| `source_code`   | implementação interna do FastAPI           | **low**  |

> No retrieval, considere filtrar `priority != 'low'` por padrão e só incluir
> `source_code` quando a pergunta for sobre implementação.

## Servir via MCP e conectar ao Claude Code

### Opção A — servidor de produção (recomendado)

O servidor está no ar em `https://mcp.pedroct.com.br/fastapi-llm-toolkit`.
Basta apontar o `.mcp.json` do seu projeto para esse endereço:

```json
{ "mcpServers": { "fastapi-kb": { "type": "http", "url": "https://mcp.pedroct.com.br/fastapi-llm-toolkit" } } }
```

### Opção B — Docker local (desenvolvimento / offline)

A stack tem **auto-seed**: o serviço `indexer` semeia o Qdrant automaticamente
na primeira subida — não é necessário rodar `build_index` manualmente.

```bash
docker compose build        # imagem fastapi-llm-toolkit:local (~1.8 GB, torch CPU)
docker compose up -d        # qdrant + indexer (auto-seed 870 chunks) + mcp-server em :8000/mcp
```

O `.mcp.json` na raiz já registra o servidor local para o **Claude Code**:

```json
{ "mcpServers": { "fastapi-kb": { "type": "http", "url": "http://localhost:8000/mcp" } } }
```

Na 1ª sessão do `claude` neste diretório, aprove o servidor (`Pending approval`);
confira com `claude mcp list`. Passo a passo completo e armadilhas: ver
`packages/mcp-server/README.md` e `CLAUDE.md` §13.

> **Usar em outro projeto FastAPI?** Para configurar um repositório consumidor
> (apontar o `.mcp.json` para o servidor + instalar as skills), veja
> [`consumer-setup.md`](consumer-setup.md).

## Configurar as Skills no Claude Code

As skills são arquivos `SKILL.md` (frontmatter `name` + `description`) em
`packages/skills/<nome>/`. O Claude Code descobre skills em `.claude/skills/`,
então ligamos os dois com symlinks — a fonte de verdade continua em
`packages/skills/`:

```bash
mkdir -p .claude/skills
ln -sfn ../../packages/skills/endpoint-scaffold    .claude/skills/endpoint-scaffold
ln -sfn ../../packages/skills/dependency-injection .claude/skills/dependency-injection
```

Os symlinks são versionados (`.claude/skills/` vai pro git), então quem clonar o
repo já recebe as skills. Para adicionar uma nova: crie
`packages/skills/<nova>/SKILL.md` e refaça o `ln -sfn` correspondente.

> As skills são carregadas no início da sessão — **reinicie o `claude`** para
> que apareçam. Confira digitando `/` (devem listar `fastapi-endpoint-scaffold`
> e `fastapi-dependency-injection`).

## Divisão de responsabilidades

| Estratégia | O que resolve | Onde mora |
|-----------|----------------|-----------|
| **RAG**   | *qual a assinatura / parâmetros de X* (muda por versão) | `packages/rag` |
| **MCP**   | *agir sobre o projeto real* (lê openapi.json, valida uso) | `packages/mcp-server` |
| **Skills**| *como fazer* (procedimento estável e citável) | `packages/skills` |

## Status

- [x] `core`: modelos + chunker (4 estágios, validado contra material real) + fontes
- [x] `rag`: coleta standalone, ingestão completa (~870 chunks, FastAPI 0.115.x)
- [x] **vector store Qdrant + embeddings locais** — indexação e retrieval validadosV
- [x] filtros de retrieval: version, symbol, kind, exclusão de source_code
- [x] `mcp-server`: **servidor FastMCP real, 4 tools, testado end-to-end** — busca na doc (`search_reference`, `get_symbol`) + introspecção do projeto (`read_project_openapi`, `list_known_versions`)
- [x] `skills`: 2 SKILL.md de exemplo
- [ ] (melhoria) busca híbrida vetorial + keyword p/ termos literais (ex.: "404")
- [ ] (melhoria) tool `validate_against_reference` cruzando projeto × doc

Pipeline completo e funcional: **coletar → ingerir → indexar → servir via MCP**.
Para conectar ao **Claude Code** (via `.mcp.json` + Docker) ou ao **Claude
Desktop**, ver `packages/mcp-server/README.md`.

Maintenance

ActivityInactive
ResponsivenessNo issues