# 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 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 '' [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 '' --format json | jq '.shortlist[].title' # Detailed run with comps finn-eiendom analyze-search '' --detail-limit 30 --with-similar --format markdown ``` ### 3.2 Drill into one listing ```bash finn-eiendom get-ad [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 [...] [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 [--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 '' [--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 '' --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 '' # find unitCode for a FINN listing finn-eiendom get-unit [--force-refresh] # fetch unit detail finn-eiendom enrich-ad [--with-similar] # FINN + Eiendom.no combined finn-eiendom build-vector # build the base64url unit_vector finn-eiendom decode-vector # decode for inspection finn-eiendom similar-units [--status RECENTLY_SOLD|FOR_SALE|CURRENT] ``` ### 3.8 Find similar to liked ```bash finn-eiendom similar-to-liked [--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 ``` 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": "", "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 `. * 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.