This commit is contained in:
Ole
2026-05-16 06:54:17 +00:00
commit 1399f61c1a
44 changed files with 6746 additions and 0 deletions
+503
View File
@@ -0,0 +1,503 @@
# USAGE.md — finn-eiendom user guide
How to use the tool day-to-day. Covers installation, every CLI command, every MCP tool, Claude Desktop integration, common workflows, environment variables, and troubleshooting.
For the why and the architecture, see [`README.md`](README.md) and [`PRD.md`](PRD.md).
---
## 1. Installation
### Requirements
* Python **3.12 or newer** (check with `python3 --version`)
* `uv` (recommended) or `pip`
* macOS, Linux, or WSL2 on Windows
### Install
```bash
git clone <your-repo-url> finn-mcp
cd finn-mcp
# Option A: uv (preferred — fast)
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
# Option B: pip
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```
Verify:
```bash
finn-eiendom --help
finn-eiendom-mcp --help # may exit immediately on stdio mode; that's fine
finn-eiendom doctor # smoke-checks cache, FINN, Eiendom.no reachability
```
### Updating
```bash
git pull
source .venv/bin/activate
uv pip install -e ".[dev]"
```
If `pyproject.toml` added dependencies, the second command picks them up.
### Global install (optional)
If you want `finn-eiendom` available system-wide without activating the venv:
```bash
uv tool install .
# or
pipx install .
```
---
## 2. First-time setup
### Set up the data directory
```bash
mkdir -p data
```
SQLite cache lives there at `data/finn.sqlite` by default. Override with `FINN_CACHE_PATH` if you want it elsewhere.
### Optional: environment file
Create `.env` in the project root for your usual settings:
```bash
FINN_CACHE_PATH=data/finn.sqlite
FINN_MAX_SEARCH_PAGES=3
FINN_DETAIL_LIMIT=20
EIENDOM_NO_ENABLED=true
EIENDOM_NO_SIMILAR_UNITS_ENABLED=true
LOG_LEVEL=INFO
```
See §7 for the full list of variables.
### Verify
```bash
finn-eiendom doctor
```
This pings the cache, reaches FINN once, reaches Eiendom.no once, and reports any failures.
---
## 3. CLI reference
Every command runs inside the activated venv.
### 3.1 Analyze a FINN search
```bash
finn-eiendom analyze-search '<finn-search-url>' [options]
```
| Option | Default | Purpose |
| ------------------- | ------- | ---------------------------------------------------------- |
| `--max-pages N` | `3` | Pages of search results to fetch. |
| `--detail-limit N` | `20` | How many listings to detail-fetch from the result set. |
| `--no-details` | off | Skip detail fetches; use only search-card data. |
| `--no-eiendom` | off | Skip Eiendom.no enrichment. |
| `--with-similar` | off | Fetch similar-units / comps for shortlisted listings. |
| `--format FMT` | `json` | `json`, `markdown`, or `table`. |
Examples:
```bash
# Triage in the terminal
finn-eiendom analyze-search 'https://www.finn.no/realestate/homes/search.html?location=0.20061&min_bedrooms=2&price_collective_to=12000000' --format table
# Full JSON for piping into jq
finn-eiendom analyze-search '<url>' --format json | jq '.shortlist[].title'
# Detailed run with comps
finn-eiendom analyze-search '<url>' --detail-limit 30 --with-similar --format markdown
```
### 3.2 Drill into one listing
```bash
finn-eiendom get-ad <finnkode> [options]
```
| Option | Default | Purpose |
| ------------------- | ------- | -------------------------------------------------- |
| `--force-refresh` | off | Bypass the 24h cache and refetch. |
| `--no-eiendom` | off | Skip Eiendom.no enrichment. |
| `--with-similar` | off | Fetch similar-units / comps. |
| `--format FMT` | `json` | `json` or `markdown`. |
```bash
finn-eiendom get-ad 462400360 --format markdown
finn-eiendom get-ad 462400360 --force-refresh --with-similar
```
### 3.3 Compare listings
```bash
finn-eiendom compare <finnkode> <finnkode> [<finnkode>...] [options]
```
| Option | Default | Purpose |
| ---------------- | ------- | -------------------------------------- |
| `--no-eiendom` | off | Skip Eiendom.no enrichment. |
| `--no-comps` | off | Skip similar-units / comps. |
| `--format FMT` | `json` | `json`, `markdown`, or `table`. |
```bash
finn-eiendom compare 462400360 461153194 --format markdown
finn-eiendom compare 462400360 461153194 462400360 --format table
```
Up to 10 finnkoder per call.
### 3.4 Feedback
```bash
finn-eiendom save-feedback <finnkode> <verdict> [--notes "..."]
```
Verdict vocabulary: `liked`, `rejected`, `interesting`, `bargain_candidate`, `risk_object`, `viewing_candidate`, `viewed`, `too_expensive`, `too_small`, `too_far_out`, `too_high_risk`, `likes_location`, `likes_layout`, `dislikes_area`.
```bash
finn-eiendom save-feedback 462400360 liked --notes "balcony, view, check wet rooms"
finn-eiendom save-feedback 461153194 rejected --notes "too far from city center"
```
`liked` verdicts feed the `similar-to-liked` command.
### 3.5 New / removed / changed listings
```bash
finn-eiendom diff '<finn-search-url>' [--format FMT]
```
Compares the current search results against the previous run for the same normalized URL and reports new finnkoder, removed finnkoder, and changed listings (price, common costs, status).
```bash
finn-eiendom diff '<url>' --format table
```
Useful as a daily cron:
```bash
0 9 * * * cd /path/to/finn-mcp && .venv/bin/finn-eiendom diff 'https://www.finn.no/...' --format markdown >> diff.log
```
### 3.6 Shortlist history
```bash
finn-eiendom shortlist [--run-id ID] [--limit N] [--format FMT]
```
Without `--run-id`, returns the latest saved shortlist.
### 3.7 Eiendom.no commands
```bash
finn-eiendom resolve-unit '<finn-listing-url>' # find unitCode for a FINN listing
finn-eiendom get-unit <unit_code> [--force-refresh] # fetch unit detail
finn-eiendom enrich-ad <finnkode> [--with-similar] # FINN + Eiendom.no combined
finn-eiendom build-vector <unit_code> # build the base64url unit_vector
finn-eiendom decode-vector <unit_vector> # decode for inspection
finn-eiendom similar-units <unit_vector> [--status RECENTLY_SOLD|FOR_SALE|CURRENT]
```
### 3.8 Find similar to liked
```bash
finn-eiendom similar-to-liked <finnkode> [--mode recommendations|comps] [--status STATUS]
```
The listing must have a `liked` feedback row. Defaults to `mode=recommendations`, `status=FOR_SALE` — i.e. find active listings similar to this one. Use `--mode comps --status RECENTLY_SOLD` to get comparable sales instead.
### 3.9 Price analysis against comps
```bash
finn-eiendom analyze-against-comps <finnkode>
```
Returns `price_position` (`below_estimate` / `within_range` / `above_estimate`), `sqm_price_position` (`cheap` / `normal` / `expensive`), `comparable_score`, and a `confidence` label.
### 3.10 Cache management
```bash
finn-eiendom cache stats # row counts and TTL summary
finn-eiendom cache clear # purge everything except feedback
finn-eiendom cache clear-html # only purge raw HTML
finn-eiendom cache clear-json # only purge raw JSON
```
Feedback is never purged by `cache clear` — feedback is permanent until explicitly deleted via SQL.
### 3.11 MCP server
```bash
finn-eiendom serve # stdio (default)
finn-eiendom serve --transport http --port 8010 # HTTP for n8n / multi-client
```
In HTTP mode the server listens on `http://127.0.0.1:8010/mcp` with operational endpoints `GET /health`, `GET /version`, `GET /debug/config`.
There's also a shorthand `finn-eiendom-mcp` that starts stdio mode directly — that's what Claude Desktop calls.
### 3.12 Misc
```bash
finn-eiendom config show # print resolved configuration
finn-eiendom config path # print SQLite cache path
finn-eiendom doctor # smoke checks
finn-eiendom version
```
---
## 4. MCP tools (for Claude Desktop / n8n / agents)
All tools use the `finn_` prefix. They mirror the CLI commands 1:1 — same defaults, same semantics.
| Tool | Purpose |
| ------------------------------------- | ---------------------------------------------------------------- |
| `finn_analyze_search` | Analyze a FINN search URL and return a ranked shortlist. |
| `finn_get_ad` | Fetch structured data for one finnkode. |
| `finn_compare_ads` | Compare multiple listings side by side. |
| `finn_save_feedback` | Store feedback/verdict/notes. |
| `finn_get_shortlist` | Fetch a stored shortlist from a previous run. |
| `finn_get_new_ads_since_last_run` | Detect new / removed / changed listings. |
| `finn_resolve_eiendom_unit` | Map FINN URL → Eiendom.no `unitCode`. |
| `finn_get_eiendom_unit` | Fetch Eiendom.no unit detail by `unitCode`. |
| `finn_enrich_ad` | Combine FINN listing + Eiendom.no enrichment. |
| `finn_build_unit_vector` | Build a `unit_vector` from a `unitCode`. |
| `finn_decode_unit_vector` | Decode a `unit_vector` for inspection. |
| `finn_get_similar_units` | Fetch comps / recommendations. |
| `finn_find_similar_to_liked_ad` | Find properties similar to one you liked. |
| `finn_analyze_ad_against_comps` | Evaluate a listing against `RECENTLY_SOLD` comps. |
Every tool accepts a `response_format` parameter (`"json"` or `"markdown"`). Errors come back as `{"error": true, "code": "<ExceptionName>", "message": "..."}`.
---
## 5. Claude Desktop setup
### Config file
* macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
* Linux: `~/.config/Claude/claude_desktop_config.json`
### Direct entry-point (recommended)
```json
{
"mcpServers": {
"finn-eiendom": {
"command": "/absolute/path/to/finn-mcp/.venv/bin/finn-eiendom-mcp",
"env": {
"FINN_CACHE_PATH": "/absolute/path/to/finn-mcp/data/finn.sqlite",
"EIENDOM_NO_ENABLED": "true",
"EIENDOM_NO_SIMILAR_UNITS_ENABLED": "true",
"LOG_LEVEL": "INFO"
}
}
}
}
```
The `command` **must** be the absolute path to the venv's `finn-eiendom-mcp` binary. Don't rely on `$PATH` here — Claude Desktop doesn't inherit your shell environment.
### Alternative: via `uv`
```json
{
"mcpServers": {
"finn-eiendom": {
"command": "uv",
"args": ["run", "finn-eiendom-mcp"],
"cwd": "/absolute/path/to/finn-mcp"
}
}
}
```
### Verify
1. Restart Claude Desktop.
2. Look for `finn-eiendom` in the MCP servers indicator (usually a hammer icon).
3. Ask in any chat: *"Use the finn-eiendom server to analyze this search: ..."*
If it doesn't show up, check the Claude Desktop logs:
* macOS: `~/Library/Logs/Claude/mcp-server-finn-eiendom.log`
* Linux: `~/.local/share/Claude/logs/mcp-server-finn-eiendom.log`
stdout output from the server is a fatal error — the server must only log to stderr.
---
## 6. Common workflows
### 6.1 Daily triage
```bash
# Morning routine
finn-eiendom diff 'https://www.finn.no/...' --format table
# Detail-fetch only what's new or changed
finn-eiendom analyze-search 'https://www.finn.no/...' --detail-limit 10 --format markdown
```
### 6.2 Weekly deep dive in Claude Desktop
> Read my latest finn-eiendom shortlist and group the top 10 by category (bargain / safe / hybel / lifestyle). For each, summarize the three most important risks and the three most important broker questions.
### 6.3 Pre-viewing prep
```bash
# Mark candidates for viewing
finn-eiendom save-feedback 462400360 viewing_candidate --notes "Saturday 14:00"
# Get the full data + comps
finn-eiendom get-ad 462400360 --with-similar --format markdown > viewing_prep_462400360.md
```
Then in Claude Desktop:
> Read the saved markdown for finnkode 462400360 and prepare a viewing checklist: wet rooms to inspect, common-costs questions, hybel-approval question, neighbor questions.
### 6.4 Comparing finalists
```bash
finn-eiendom compare 462400360 461153194 459333210 --format markdown > finalists.md
```
### 6.5 Build a recommendation set from liked properties
```bash
# After you've liked a few
finn-eiendom save-feedback 462400360 liked
finn-eiendom save-feedback 461153194 liked
# Get recommendations similar to each
finn-eiendom similar-to-liked 462400360 --mode recommendations --status FOR_SALE
finn-eiendom similar-to-liked 461153194 --mode recommendations --status FOR_SALE
```
---
## 7. Environment variables
| Variable | Default | Purpose |
| ----------------------------------------- | -------------------------------: | -------------------------------- |
| `FINN_CACHE_PATH` | `data/finn.sqlite` | SQLite DB path |
| `FINN_MAX_SEARCH_PAGES` | `3` | Max search pages per analyze |
| `FINN_DETAIL_LIMIT` | `20` | Max detail fetches per analyze |
| `FINN_REQUEST_DELAY_SECONDS` | `2` | Seconds between FINN requests |
| `FINN_USER_AGENT` | `personal-finn-eiendom-analyzer/0.1` | HTTP User-Agent |
| `FINN_CACHE_TTL_SEARCH_MINUTES` | `60` | Search cache TTL |
| `FINN_CACHE_TTL_AD_HOURS` | `24` | Listing cache TTL |
| `EIENDOM_NO_ENABLED` | `true` | Enable Eiendom.no enrichment |
| `EIENDOM_NO_BASE_URL` | `https://api.eiendom.no/api/v1` | API base URL |
| `EIENDOM_NO_CACHE_TTL_HOURS` | `24` | Unit/similar cache TTL |
| `EIENDOM_NO_REQUEST_DELAY_SECONDS` | `1` | Seconds between Eiendom.no calls |
| `EIENDOM_NO_SIMILAR_UNITS_ENABLED` | `true` | Enable similar-units |
| `EIENDOM_NO_SIMILAR_UNITS_DEFAULT_STATUS` | `RECENTLY_SOLD` | Default comps status |
| `HJEMLA_ENABLED` | `false` | Enable optional Hjemla API |
| `LOG_LEVEL` | `INFO` | Log level |
| `MCP_TRANSPORT` | `stdio` | `stdio` or `streamable_http` |
| `MCP_HTTP_HOST` | `127.0.0.1` | HTTP bind address |
| `MCP_HTTP_PORT` | `8010` | HTTP port |
Set them in `.env`, in your shell, or in the Claude Desktop `env` block per §5.
---
## 8. Troubleshooting
### Claude Desktop doesn't see the server
1. The `command` path must be absolute and point at the venv's binary.
2. Check `~/Library/Logs/Claude/mcp-server-finn-eiendom.log` (macOS) for a Python traceback.
3. The server **must not** write to stdout — any `print()` in the code breaks JSON-RPC. If you're hacking on it and see a frame parse error, that's the cause.
4. Restart Claude Desktop after config changes (`Cmd+Q`, not just close the window).
### "Module not found" when running CLI
The venv isn't activated, or the package isn't installed in editable mode.
```bash
source .venv/bin/activate
uv pip install -e ".[dev]"
```
### Eiendom.no enrichment is `unavailable`
This is graceful degradation when:
* The FINN URL can't be matched to a `unitCode` (rare, but happens for unusual addresses).
* Eiendom.no rate-limited or returned 5xx.
* The unit was deleted from Eiendom.no's index.
Check the log for the warning. The listing analysis continues without enrichment.
### Similar-units returns nothing
* Verify `EIENDOM_NO_SIMILAR_UNITS_ENABLED=true`.
* The `unit_vector` might be empty / malformed — check `finn-eiendom decode-vector <unit_vector>`.
* Try `--status FOR_SALE` if `RECENTLY_SOLD` is sparse, or vice versa.
### Slow first run
The first analyze fills the cache. Subsequent runs are much faster. Tune `FINN_REQUEST_DELAY_SECONDS` and `EIENDOM_NO_REQUEST_DELAY_SECONDS` if you're impatient — but don't drop them too low, the whole point of caching is to be polite.
### Stale results
Cache TTLs:
* Search: 60 minutes
* FINN listing: 24 hours
* Eiendom.no unit: 24 hours
* Similar-units: 24 hours
Force a refresh with `--force-refresh` on `get-ad` or `get-unit`, or wipe with `finn-eiendom cache clear`.
### `pytest` fails after pulling new changes
```bash
source .venv/bin/activate
uv pip install -e ".[dev]" # re-sync dependencies
pytest -x # find the first failure
```
If a test fails with a network-related error, that's a bug — tests should never hit the network. Report it.
---
## 9. What this tool is not
* Not a public API. Don't expose the HTTP transport on the open internet.
* Not financial, legal, or valuation advice. Scores and estimates are decision support.
* Not a bidding agent. It will never contact a broker or place a bid for you.
* Not a crawler. Use it for the searches you'd be manually browsing anyway — at your own pace.
* Not a substitute for a real condition report (`tilstandsrapport`), a real lawyer, or a real broker.
---
## 10. Getting help
* [`README.md`](README.md) — overview
* [`PRD.md`](PRD.md) — full product spec and architecture
* [`AGENTS.md`](AGENTS.md) — workflow rules for contributors
* [`.github/instructions/*.md`](.github/instructions/) — per-topic conventions
For bugs, open an issue in the repo with: the exact command run, the full traceback or unexpected output, the version (`finn-eiendom version`), and a redacted FINN URL if relevant.