9.2 KiB
9.2 KiB
PROJECT.md — module map
The repo at a glance. For the why and the rules, read PRD.md §12 and §17. For the workflow, read AGENTS.md.
Source tree
finn-mcp/
├── pyproject.toml
├── Makefile
├── README.md ← user-facing overview
├── USAGE.md ← full user guide
├── PRD.md ← product spec + architecture (§17 = constitution)
├── PROJECT.md ← this file
├── AGENTS.md ← workflow for AI agents and contributors
├── CLEANUP.md ← pre-Phase-2 cleanup runbook
├── IMPLEMENTATION.md ← Phase 2 build runbook (12 steps)
│
├── .github/
│ ├── copilot-instructions.md
│ └── instructions/
│ ├── python.instructions.md
│ ├── mcp.instructions.md
│ ├── cli.instructions.md
│ ├── tests.instructions.md
│ ├── clean-code.instructions.md
│ └── docs.instructions.md ← context7 lookup rules
│
├── finn_eiendom/ ← the package
│ ├── __init__.py
│ ├── __main__.py ← python -m finn_eiendom → CLI
│ ├── config.py ← env vars, defaults, TTLs
│ ├── models.py ← Pydantic v2 models
│ ├── parser.py ← Norwegian number/area/URL/finnkode normalization
│ ├── http.py ← async httpx client w/ retry + delay
│ ├── cache.py ← SQLite schema + persistence
│ ├── search.py ← FINN search HTML parsing
│ ├── ad.py ← FINN listing HTML parsing
│ ├── eiendom_no.py ← Eiendom.no unit search/detail, unit_vector, comps
│ ├── scoring.py ← score model + classifications
│ ├── feedback.py ← verdicts + soft preference signal
│ ├── analysis.py ← shortlist + summary assembly
│ ├── service.py ← get_or_fetch_* + thin facade for MCP and CLI
│ ├── formatting.py ← render_* helpers (json/markdown/table) — shared by MCP and CLI
│ ├── mcp_server.py ← FastMCP wrappers around service.py
│ └── cli.py ← typer wrappers around service.py
│
├── tests/
│ ├── conftest.py
│ ├── fixtures.py
│ ├── fixtures/ ← HTML + JSON samples
│ ├── test_parser.py
│ ├── test_search.py
│ ├── test_ad.py
│ ├── test_eiendom_no.py
│ ├── test_scoring.py
│ ├── test_cache.py
│ ├── test_http.py ← retry + delay behavior
│ ├── test_service.py ← get_or_fetch_* + analyze_*
│ ├── test_formatting.py ← render_* roundtrips
│ ├── test_models.py ← Pydantic v2 roundtrips
│ ├── test_mcp_server.py ← tool registration + error envelope
│ ├── test_cli.py ← Typer CliRunner
│ └── test_architecture.py ← import-graph invariants (PRD A10)
│
└── data/ ← gitignored; SQLite cache lives here
└── finn.sqlite
Module responsibilities
Single-home rule: every concern lives in exactly one module. See PRD.md §17.2 for the full table.
| Module | Owns | Imports allowed |
|---|---|---|
config.py |
env-var loading, defaults, TTL constants | stdlib |
models.py |
Pydantic v2 models | stdlib, pydantic |
parser.py |
Norwegian text normalization (numbers, dates, URLs, finnkode) | stdlib |
http.py |
async httpx.AsyncClient, retry on 5xx, delay, user-agent |
stdlib, httpx |
cache.py |
SQLite schema, reads, writes, TTL | stdlib, sqlite3, models |
search.py |
FINN search HTML → cards (BeautifulSoup) | stdlib, bs4, parser, http, cache, models |
ad.py |
FINN listing HTML → FinnAd (BeautifulSoup) |
stdlib, bs4, parser, http, cache, models |
eiendom_no.py |
Eiendom.no unit search/detail, unit_vector, similar-units (msgpack) | stdlib, msgpack, http, cache, models |
scoring.py |
9 score components, total clamping, category classifier | stdlib, models |
feedback.py |
feedback storage and retrieval | stdlib, cache, models |
analysis.py |
shortlist + summary assembly | stdlib, search, ad, eiendom_no, scoring, feedback |
service.py |
cache-aware orchestration; the only place that combines fetch + cache | stdlib, config, cache, analysis, ad, eiendom_no, feedback, scoring, models |
formatting.py |
render_* helpers (json/markdown/table) | stdlib, models |
mcp_server.py |
FastMCP tool definitions, error wrapping, stdio/HTTP entry | stdlib, mcp, pydantic, service, formatting, config, models |
cli.py |
typer command definitions, --format dispatch | stdlib, typer, service, formatting, config, models |
mcp_server.py and cli.py are siblings — they never import each other. service.py never imports mcp_server or cli. tests/test_architecture.py enforces all of this.
Entry points
Defined in pyproject.toml:
[project.scripts]
finn-eiendom-mcp = "finn_eiendom.mcp_server:main"
finn-eiendom = "finn_eiendom.cli:app"
So you have:
finn-eiendom-mcp— MCP server over stdio (what Claude Desktop calls).finn-eiendom— CLI with all subcommands.python -m finn_eiendom— same asfinn-eiendom(via__main__.py).import finn_eiendom— the library, for tests and notebooks.
Dependency graph
cli.py mcp_server.py
↓ ↓
└──> formatting.py <──┘
│
↓
service.py
↓
analysis.py
↓
┌───────────┼──────────────┐
↓ ↓ ↓
search.py ad.py eiendom_no.py scoring.py feedback.py
│ │ │ │ │
↓ ↓ ↓ ↓ ↓
parser.py parser.py cache.py models.py cache.py
│ │ │
↓ ↓ ↓
http.py http.py http.py
Bottom layer: parser.py, http.py, cache.py, models.py, config.py. They depend only on stdlib + one third-party library each.
The graph is acyclic and points downward. Every arrow can be drawn; no arrow can be drawn upward.
Where to add things
| You want to… | Add it to… |
|---|---|
| Parse a new FINN field | ad.py or search.py + models.py |
| Add a new score component | scoring.py |
| Add a new env var | config.py |
| Add a new MCP tool | mcp_server.py (after service.py) |
| Add a new CLI command | cli.py (after service.py) |
| Change how something renders | formatting.py |
| Add a new orchestration / workflow | service.py (then add MCP + CLI) |
| Speak to a new external API | new module next to eiendom_no.py |
| Add a new SQLite table | cache.py |
For anything else — read PRD.md §17.2 and §17.7.