178 lines
6.4 KiB
Markdown
178 lines
6.4 KiB
Markdown
# AGENTS.md — Workflow for AI agents on finn-eiendom-mcp
|
|
|
|
This is the master doc for any AI agent (Claude, Copilot, Cursor, etc.) working in this repo. Read this first, then the more specific files it references.
|
|
|
|
---
|
|
|
|
## Read order
|
|
|
|
Before changing code, read:
|
|
|
|
1. **`PRD.md`** — what we're building and why. Especially §17 ("Code ownership and anti-duplication") — that section is the constitution.
|
|
2. **`PROJECT.md`** — module map.
|
|
3. This file — workflow.
|
|
4. The relevant `.github/instructions/*.md`:
|
|
* `python.instructions.md` — Python conventions.
|
|
* `mcp.instructions.md` — MCP tool rules.
|
|
* `cli.instructions.md` — CLI command rules.
|
|
* `tests.instructions.md` — testing conventions.
|
|
* `clean-code.instructions.md` — best practices and DRY enforcement.
|
|
* `docs.instructions.md` — when and how to use the **context7** MCP server for library documentation.
|
|
|
|
If something in code contradicts the PRD, the PRD wins. If you change behavior, update both the PRD and the relevant instruction file in the same change.
|
|
|
|
---
|
|
|
|
## Runtime — local venv (default)
|
|
|
|
This project runs in a project-local virtualenv. Docker is supported for packaging but is not required for development.
|
|
|
|
### One-time setup
|
|
|
|
```bash
|
|
# from the project root
|
|
uv venv # or: python3.12 -m venv .venv
|
|
source .venv/bin/activate
|
|
uv pip install -e ".[dev]" # or: pip install -e ".[dev]"
|
|
```
|
|
|
|
Python **3.12+** is required.
|
|
|
|
### Daily commands
|
|
|
|
All commands are run inside the activated `.venv`:
|
|
|
|
```bash
|
|
pytest # tests
|
|
ruff check . # lint
|
|
ruff format . # format
|
|
mypy finn_eiendom # type-check
|
|
finn-eiendom --help # CLI entrypoint
|
|
finn-eiendom-mcp # MCP server (stdio)
|
|
finn-eiendom serve --transport http --port 8010 # MCP server (HTTP)
|
|
```
|
|
|
|
### Never
|
|
|
|
* Install packages globally (`pip install ...` outside a venv).
|
|
* Use `sudo pip`.
|
|
* Mutate the host Python.
|
|
* Add dependencies without updating `pyproject.toml`.
|
|
|
|
### Adding a dependency
|
|
|
|
```bash
|
|
uv pip install <package> # ad-hoc, then:
|
|
# edit pyproject.toml to record it
|
|
uv pip install -e ".[dev]" # reinstall in editable mode
|
|
```
|
|
|
|
---
|
|
|
|
## Architecture in one screen
|
|
|
|
```
|
|
cli.py (typer) mcp_server.py (FastMCP) ← thin, parallel front ends
|
|
\ /
|
|
\ /
|
|
service.py ← orchestration: get_or_fetch, analyze_*
|
|
↓
|
|
analysis.py ← shortlist + summary
|
|
↓
|
|
search / ad / eiendom_no / scoring / feedback
|
|
↓
|
|
parser / http / cache
|
|
↓
|
|
FINN HTML + Eiendom.no JSON + SQLite
|
|
```
|
|
|
|
`formatting.py` sits next to `service.py` and is shared by both CLI and MCP for `json`, `markdown`, and `table` rendering.
|
|
|
|
**The single-home rule:** every piece of logic has exactly one home. If you're tempted to add it in two places, you're wrong about one — push it down a layer and call it from both. See `PRD.md` §17.2 for the full ownership table.
|
|
|
|
---
|
|
|
|
## The five hard rules
|
|
|
|
These are non-negotiable. Architecture tests in `tests/test_architecture.py` enforce them.
|
|
|
|
1. **`mcp_server.py` and `cli.py` are siblings.** They never call each other. Both call only `service`, `formatting`, `models`, and `config`.
|
|
2. **`service.py` is the only place that combines cache + fetch.** Nothing above it touches HTTP or SQLite directly.
|
|
3. **`httpx` lives in `http.py`. Nowhere else.**
|
|
4. **`sqlite3` lives in `cache.py`. Nowhere else.**
|
|
5. **Output formatting lives in `formatting.py`.** No inline rendering in CLI or MCP tool bodies.
|
|
|
|
If you have to break one of these to ship a feature, the feature is wrong — fix the design first.
|
|
|
|
---
|
|
|
|
## Adding a feature — the checklist
|
|
|
|
For any new tool / command / behavior:
|
|
|
|
1. Decide the home using the table in `PRD.md` §17.2.
|
|
2. Write the function in `service.py` (or extend `analysis.py` if it's pure orchestration).
|
|
3. Add a test in `tests/test_service.py`.
|
|
4. Add a thin MCP tool in `mcp_server.py` — `response_format` aware.
|
|
5. Add a thin CLI command in `cli.py` — `--format` aware.
|
|
6. Add the renderer in `formatting.py` if output is non-trivial.
|
|
7. Add tests in `tests/test_mcp_server.py` and `tests/test_cli.py`.
|
|
8. Update `PRD.md` and any affected `.github/instructions/*.md`.
|
|
|
|
If steps 4 or 5 need more than ~20 lines, logic has leaked out of the service layer. Push it back down.
|
|
|
|
---
|
|
|
|
## Clean code
|
|
|
|
See `.github/instructions/clean-code.instructions.md`. Highlights:
|
|
|
|
* Type hints everywhere.
|
|
* Functions stay small; one job per function.
|
|
* Names describe intent (`get_or_fetch_ad`, not `process`).
|
|
* Comments explain **why**, never **what** the code already says.
|
|
* DRY: if you write the same regex / SQL / format string twice, extract it.
|
|
* Errors fail loudly with actionable messages. No silent `except: pass`.
|
|
* No dead code, no commented-out blocks left in the tree.
|
|
|
|
---
|
|
|
|
## Documentation lookups — use context7
|
|
|
|
When uncertain about a library's API (FastMCP decorators, Pydantic v2 validators, Typer command patterns, httpx async, msgpack, pytest-asyncio, respx, BeautifulSoup selectors, etc.), **use the `context7` MCP server**. Do not guess from training-data memory.
|
|
|
|
Pattern (full details in `.github/instructions/docs.instructions.md`):
|
|
|
|
1. `context7:resolve-library-id` with the library name → get the canonical ID.
|
|
2. `context7:query-docs` with that ID + a focused topic.
|
|
|
|
Use context7 *before* writing the code, not after a test fails. If context7 returns nothing useful, search the library's official docs, then write the smallest possible spike to verify.
|
|
|
|
---
|
|
|
|
## Safety and compliance
|
|
|
|
* Private, low-frequency use only.
|
|
* Respect FINN / Eiendom.no rate limits and bot protection.
|
|
* Cache aggressively; never bulk-harvest.
|
|
* stdio MCP servers log to **stderr only** — anything on stdout breaks the JSON-RPC frame.
|
|
* Scores and estimates are decision support, never legal / technical / financial advice.
|
|
|
|
---
|
|
|
|
## Implementation order (Phase 2)
|
|
|
|
Follow `PRD.md` §29 step-by-step. Each step is independently mergeable:
|
|
|
|
1. Switch dev workflow to local venv + update instruction files (this change).
|
|
2. Pydantic v2 cleanup.
|
|
3. Service layer + tests.
|
|
4. Formatting layer + tests.
|
|
5. HTTP retry on 5xx + tests.
|
|
6. Replace FastAPI with FastMCP stdio server.
|
|
7. CLI with typer.
|
|
8. Diff workflow.
|
|
9. Compare workflow.
|
|
10. Similar-to-liked.
|
|
11. Architecture tests.
|
|
12. README + Claude Desktop config. |