ed8892fb2b
Replace the legacy English MCP-only README with a comprehensive Hebrew README covering the full Web + MCP product: features, architecture, 3-tier appraisals, smart "same street" inference, RTL hardening guide, deployment topology, all REST endpoints with curl examples. Also update pyproject.toml description to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
387 lines
16 KiB
Markdown
387 lines
16 KiB
Markdown
# Nadlan-MCP — חיפוש נדל"ן ושמאי מכריע
|
||
|
||
כלי web ו-MCP חינמי וקוד פתוח לחיפוש מידע נדל"ני בישראל: עסקאות נדל"ן עדכניות מ-Govmap והחלטות שמאי מכריע ממשרד המשפטים — בממשק עברי מלא עם תמיכת RTL.
|
||
|
||
> **TL;DR:** הכנס כתובת, גוש+חלקה או שם של שמאי מכריע — מקבל בו-זמנית עסקאות באזור והחלטות שומה רלוונטיות, כולל הורדת PDF.
|
||
|
||
---
|
||
|
||
## תוכן עניינים
|
||
|
||
- [מה הכלי עושה](#מה-הכלי-עושה)
|
||
- [שני ממשקים — Web ו-MCP](#שני-ממשקים--web-ו-mcp)
|
||
- [תכונות עיקריות](#תכונות-עיקריות)
|
||
- [אופני חיפוש](#אופני-חיפוש)
|
||
- [ארכיטקטורה](#ארכיטקטורה)
|
||
- [התקנה והפעלה](#התקנה-והפעלה)
|
||
- [API Endpoints](#api-endpoints)
|
||
- [פריסה (Deployment)](#פריסה-deployment)
|
||
- [פיתוח](#פיתוח)
|
||
- [ניהול בעיות RTL](#ניהול-בעיות-rtl)
|
||
- [רישוי](#רישוי)
|
||
|
||
---
|
||
|
||
## מה הכלי עושה
|
||
|
||
הכלי מחבר בין שני מקורות מידע ממשלתיים שלא היו זמינים יחד עד היום:
|
||
|
||
| מקור | מה מספק | תדירות עדכון |
|
||
|------|---------|--------------|
|
||
| **Govmap** (`www.govmap.gov.il`) | עסקאות נדל"ן רשומות מ-2010 ואילך — מחיר, שטח, חדרים, קומה, גוש/חלקה/תת-חלקה | חודשי |
|
||
| **משרד המשפטים** (`pub-justice.openapi.gov.il`) | החלטות שמאי מכריע — שומות הכרעה, ערעורים, היטלי השבחה, פיצויי הפקעה, וכד' | שוטף |
|
||
|
||
המידע מוצג בטבלה אחת, מסונכרן בין המקורות לפי גוש/חלקה, עם קישורים ישירים ל-PDF של ההחלטות.
|
||
|
||
---
|
||
|
||
## שני ממשקים — Web ו-MCP
|
||
|
||
### 1. ממשק Web (React)
|
||
ממשק משתמש מלא לבני אדם:
|
||
|
||
```
|
||
https://nadlan-mcp.dev.marcus-law.co.il
|
||
```
|
||
|
||
טופס חיפוש, שתי טבלאות (עסקאות + שומות), פתיחה והורדה של PDF, RTL מלא.
|
||
|
||
### 2. ממשק MCP (Model Context Protocol)
|
||
שרת MCP ל-LLMs ול-AI agents — מאפשר ל-Claude / ChatGPT / כל סוכן AI לחפש בעצמו מידע נדל"ני. נחשף ב-`/mcp` כ-streamable HTTP. כולל 10 כלים:
|
||
|
||
- `autocomplete_address` — חיפוש כתובת
|
||
- `find_recent_deals_for_address` — עסקאות סביב כתובת
|
||
- `analyze_market_trends` — ניתוח מגמות מחירים
|
||
- `compare_addresses` — השוואה בין שתי כתובות
|
||
- `get_valuation_comparables` — השוואות שמאיות
|
||
- `get_deal_statistics` — סטטיסטיקה מצרפית
|
||
- `get_market_activity_metrics` — מדדי פעילות שוק
|
||
- ועוד.
|
||
|
||
ראה `nadlan_mcp/fastmcp_server.py`.
|
||
|
||
---
|
||
|
||
## תכונות עיקריות
|
||
|
||
### 🔍 שלושה אופני חיפוש בו-זמנית
|
||
חיפוש לפי **כתובת** (`קטלב 5 צור הדסה`), לפי **גוש+חלקה** (`גוש 6212 חלקה 894`) או לפי **שם של שמאי מכריע** (`לוי גלבוע`). הממשק מתאים את עצמו אוטומטית.
|
||
|
||
### 🏢 זיהוי "אותו רחוב" חכם
|
||
כשמחפשים לפי גוש+חלקה, ה-Govmap מחזיר עסקאות מהפוליגון של הרחוב — אבל פוליגון יכול להכיל מספר רחובות. הפתרון שלנו: מציאת העסקה עם מספר חלקה הקרוב ביותר לחלקה המחופשת בתוך אותו גוש — סביר מאוד שהיא באותו רחוב. כך אנחנו מסמנים נכון מה "אותו רחוב" ומה "סביבה קרובה".
|
||
|
||
### 📊 שלוש רמות לשומות שמאי מכריע
|
||
כל חיפוש לפי גוש+חלקה מחזיר אוטומטית שלוש רמות:
|
||
1. **החלטות תואמות** — בדיוק הגוש+חלקה שחיפשת
|
||
2. **החלטות נוספות באותו גוש** — חלקות אחרות באותו גוש
|
||
3. **החלטות בגושים סמוכים** — גוש ±2
|
||
|
||
דה-דופליקציה מובנית מבטיחה שהחלטה לא תופיע פעמיים.
|
||
|
||
### 📄 PDF Proxy
|
||
החלטות שמאי מכריע מאוחסנות בשרת ממשלתי שדורש header של `x-client-id`. דפדפן רגיל לא יכול להוריד אותן ישירות. הכלי מספק proxy endpoint שמטפל בזה — עם whitelist של hosts מותרים למניעת SSRF.
|
||
|
||
### 🛡️ עקיפת WAF של F5
|
||
ה-API של משרד המשפטים מוגן ב-WAF של F5 שחוסם בקשות "לא דפדפניות". הכלי משתמש ב-`curl_cffi` עם impersonation של Chrome 120 (TLS fingerprint תואם דפדפן אמיתי) כדי לעבור את ה-WAF.
|
||
|
||
### 🌐 RTL מלא
|
||
תמיכה מלאה בעברית — כולל `DirectionProvider` של Radix, `dir="rtl"` מפורש על body/tables/Tabs, ועיצוב Tailwind עם classes לוגיים (`text-start`/`text-end`/`ms-*`/`me-*`).
|
||
|
||
### ⚡ זיהוי חריגים (Outlier Detection)
|
||
ניתוחים סטטיסטיים משתמשים בפילטר IQR + סינון אחוזי + גבולות קשיחים, להסרת עסקאות עם נתונים שגויים (טעויות הקלדה, מכירות חלקיות, מקרים חריגים).
|
||
|
||
---
|
||
|
||
## אופני חיפוש
|
||
|
||
### חיפוש לפי כתובת
|
||
```
|
||
קטלב 5 צור הדסה
|
||
הילדסהיימר 14 תל אביב
|
||
ויצמן 10 חיפה
|
||
```
|
||
מחזיר את העסקאות באזור + השומות התואמות לגוש שזוהה.
|
||
|
||
### חיפוש לפי גוש + חלקה
|
||
מתאים כשאין כתובת מדויקת או כשמחפשים פרצל גולמי:
|
||
```
|
||
גוש: 6212 חלקה: 894
|
||
```
|
||
פנימית, נשלח ל-Govmap בפורמט `גוש N חלקה M` (Govmap מזהה את הפורמט הזה ב-autocomplete).
|
||
|
||
### חיפוש לפי שמאי מכריע
|
||
מציג את כל ההחלטות שאותו שמאי הכריע בהן:
|
||
```
|
||
לוי גלבוע
|
||
גלית עוזרי
|
||
```
|
||
לא מציג עסקאות (אין רלוונטיות אזורית).
|
||
|
||
---
|
||
|
||
## ארכיטקטורה
|
||
|
||
```
|
||
┌────────────────────────────────────────────────────────────────┐
|
||
│ דפדפן (React 19 + Vite) │
|
||
│ ─ SearchBar (3 modes) │
|
||
│ ─ DealsTable (proximity inference, gush/חלקה columns) │
|
||
│ ─ AppraisalsTable (3 tiers: exact / same block / nearby) │
|
||
│ ─ TanStack Query, shadcn/ui, RTL DirectionProvider │
|
||
└──────────────────────────────────┬─────────────────────────────┘
|
||
│ REST + JSON
|
||
▼
|
||
┌────────────────────────────────────────────────────────────────┐
|
||
│ FastAPI + FastMCP (Python 3.13) │
|
||
│ │
|
||
│ /api/search/address ─→ Govmap autocomplete │
|
||
│ /api/search/deals ─→ Govmap deals (radius+polygon) │
|
||
│ /api/search/appraisals ─→ gov.il SearchDecisions (3 tiers) │
|
||
│ /api/appraisals/pdf ─→ Streaming PDF proxy w/ x-client-id │
|
||
│ /mcp ─→ FastMCP streamable-http (LLMs) │
|
||
│ / ─→ Static (React build) │
|
||
└────────────────────────────────────────────────────────────────┘
|
||
│ │
|
||
▼ ▼
|
||
┌──────────────────┐ ┌────────────────────────────┐
|
||
│ www.govmap.gov │ │ pub-justice.openapi.gov.il │
|
||
│ (no auth) │ │ (x-client-id header, │
|
||
│ │ │ curl_cffi Chrome120 TLS) │
|
||
└──────────────────┘ └────────────────────────────┘
|
||
```
|
||
|
||
**שכבות הקוד:**
|
||
|
||
```
|
||
nadlan-mcp/
|
||
├── nadlan_mcp/
|
||
│ ├── fastmcp_server.py # FastMCP tools (10) + clients
|
||
│ ├── web_app.py # FastAPI app + REST endpoints + static
|
||
│ ├── config.py # GovmapConfig + env vars
|
||
│ ├── govmap/ # Govmap API client (Pydantic v2 models)
|
||
│ │ ├── client.py # HTTP, retry, rate limiting
|
||
│ │ ├── models.py # Deal, AutocompleteResponse, etc.
|
||
│ │ ├── filters.py # Deal filtering
|
||
│ │ ├── statistics.py # Statistical analysis + outlier filtering
|
||
│ │ ├── market_analysis.py # Trends, comparables
|
||
│ │ └── outlier_detection.py
|
||
│ └── govil/ # gov.il decisive-appraiser client
|
||
│ ├── client.py # curl_cffi w/ Chrome impersonation
|
||
│ └── models.py # AppraisalDecision, AppraisalDocument
|
||
├── web/ # React 19 + Vite + shadcn/ui frontend
|
||
│ ├── src/
|
||
│ │ ├── App.tsx
|
||
│ │ ├── api/{client,types}.ts
|
||
│ │ └── components/
|
||
│ │ ├── SearchBar.tsx
|
||
│ │ ├── DealsTable.tsx
|
||
│ │ ├── AppraisalsTable.tsx
|
||
│ │ └── ui/ # shadcn-ui primitives (RTL-patched)
|
||
│ └── package.json
|
||
├── tests/ # 314 tests, 84% coverage
|
||
├── Dockerfile # Multi-stage: node build → python runtime
|
||
└── .gitea/workflows/deploy.yaml # CI/CD: build → registry → Coolify
|
||
```
|
||
|
||
---
|
||
|
||
## התקנה והפעלה
|
||
|
||
### דרישות
|
||
- Python 3.13+
|
||
- Node 22+ (לבניית ה-frontend)
|
||
- pip / venv
|
||
|
||
### הרצה לוקלית
|
||
|
||
```bash
|
||
# 1) שכפול
|
||
git clone https://gitea.dev.marcus-law.co.il/mcp-servers/nadlan-mcp.git
|
||
cd nadlan-mcp
|
||
|
||
# 2) Python — סביבה וירטואלית
|
||
python3 -m venv venv
|
||
source venv/bin/activate
|
||
pip install -e .
|
||
|
||
# 3) בניית ה-frontend
|
||
cd web
|
||
npm install
|
||
npm run build
|
||
cd ..
|
||
|
||
# 4) הרצת השרת
|
||
PORT=8000 python run_http_server.py
|
||
```
|
||
|
||
פתח ב-`http://127.0.0.1:8000`.
|
||
|
||
### הרצה עם Docker
|
||
|
||
```bash
|
||
docker build -t nadlan-mcp .
|
||
docker run -p 8000:8000 nadlan-mcp
|
||
```
|
||
|
||
### משתני סביבה
|
||
|
||
| משתנה | ברירת מחדל | תיאור |
|
||
|--------|------------|--------|
|
||
| `PORT` | `8000` | פורט השרת |
|
||
| `GOVMAP_BASE_URL` | `https://www.govmap.gov.il/api/` | בסיס API של Govmap |
|
||
| `GOVMAP_REQUESTS_PER_SECOND` | `5.0` | rate-limit ל-Govmap |
|
||
| `GOVMAP_MAX_RETRIES` | `3` | retry exponential backoff |
|
||
| `GOVMAP_DEFAULT_RADIUS` | `50` | רדיוס ברירת מחדל (מטרים) |
|
||
| `ANALYSIS_OUTLIER_METHOD` | `iqr` | שיטת זיהוי חריגים (`iqr`/`percent`/`none`) |
|
||
| `ANALYSIS_PRICE_PER_SQM_MIN` | `1000` | גבול תחתון נמוך מדי למחיר/מ"ר |
|
||
| `ANALYSIS_PRICE_PER_SQM_MAX` | `100000` | גבול עליון |
|
||
|
||
ראה רשימה מלאה ב-`nadlan_mcp/config.py`.
|
||
|
||
---
|
||
|
||
## API Endpoints
|
||
|
||
כל ה-endpoints נחשפים תחת `/api/` (גם `/api/docs` לתיעוד OpenAPI/Swagger).
|
||
|
||
### `GET /api/search/address?q=<query>`
|
||
Autocomplete של כתובת — מחזיר קואורדינטות + גוש/חלקה אם זוהו.
|
||
|
||
```bash
|
||
curl "https://nadlan-mcp.dev.marcus-law.co.il/api/search/address?q=הילדסהיימר%2014%20תל%20אביב"
|
||
```
|
||
|
||
### `POST /api/search/deals`
|
||
חיפוש עסקאות סביב כתובת.
|
||
|
||
```json
|
||
{
|
||
"address": "גוש 6212 חלקה 894",
|
||
"years_back": 3,
|
||
"radius_meters": 100,
|
||
"max_deals": 50,
|
||
"deal_type": 2
|
||
}
|
||
```
|
||
|
||
תגובה כוללת `search.resolved_address` (הטקסט המפוענח של Govmap), `total`, ו-`deals[]` עם השדות `streetNameHeb`, `houseNum`, `gushNum`, `parcelNum`, `subParcelNum`, `deal_amount`, `asset_area`, `price_per_sqm`, `deal_source` (`same_building` / `street` / `neighborhood`), ועוד.
|
||
|
||
### `POST /api/search/appraisals`
|
||
חיפוש החלטות שמאי מכריע. כשמעבירים `include_nearby: true` עם block, מחזיר 3 buckets:
|
||
|
||
```json
|
||
{
|
||
"block": "6212",
|
||
"plot": "894",
|
||
"max_results": 30,
|
||
"include_nearby": true,
|
||
"nearby_block_radius": 2
|
||
}
|
||
```
|
||
|
||
תגובה:
|
||
```json
|
||
{
|
||
"total_in_db": 13420,
|
||
"returned": 5,
|
||
"decisions": [...],
|
||
"same_block": { "count": 12, "decisions": [...] },
|
||
"nearby_blocks": { "count": 8, "decisions": [...] }
|
||
}
|
||
```
|
||
|
||
### `GET /api/appraisals/pdf?url=<encoded>`
|
||
Proxy ל-PDF של החלטה. רק hosts ב-whitelist (`pub-justice.openapi.gov.il`). הוסף `&download=1` לכפיית הורדה.
|
||
|
||
### `/mcp`
|
||
FastMCP streamable-http endpoint — לחיבור LLMs ו-AI agents.
|
||
|
||
---
|
||
|
||
## פריסה (Deployment)
|
||
|
||
הפריסה ל-`nadlan-mcp.dev.marcus-law.co.il` אוטומטית דרך CI/CD:
|
||
|
||
```
|
||
git push → Gitea Actions → build Docker image
|
||
→ push to private registry
|
||
→ trigger Coolify webhook
|
||
→ pull + redeploy
|
||
```
|
||
|
||
ראה `.gitea/workflows/deploy.yaml`.
|
||
|
||
**Stack:**
|
||
- Coolify (orchestration)
|
||
- Traefik 3.6 (reverse proxy + Let's Encrypt)
|
||
- Docker registry פרטי
|
||
- Python 3.13 slim image
|
||
|
||
---
|
||
|
||
## פיתוח
|
||
|
||
### בדיקות
|
||
|
||
```bash
|
||
# כל הבדיקות
|
||
pytest
|
||
|
||
# רק unit tests
|
||
pytest -m unit
|
||
|
||
# כיסוי
|
||
pytest --cov=nadlan_mcp
|
||
|
||
# בדיקת איכות מלאה (lint + format + tests)
|
||
./check-quality.sh
|
||
```
|
||
|
||
### Linting
|
||
|
||
```bash
|
||
ruff format . # פורמט
|
||
ruff check . --fix # תיקון אוטומטי
|
||
```
|
||
|
||
### פיתוח Frontend
|
||
|
||
```bash
|
||
cd web
|
||
npm run dev # Vite dev server (HMR)
|
||
npm run typecheck # tsc --noEmit
|
||
npm run lint
|
||
```
|
||
|
||
---
|
||
|
||
## ניהול בעיות RTL
|
||
|
||
זיהינו והתגברנו על מספר בעיות RTL בדרך הקשה. אם אתה בונה אפליקציית React בעברית, מומלץ:
|
||
|
||
1. **הגדרה כפולה:** `<html dir="rtl">` **וגם** `<body dir="rtl">` — חלק מהספריות בודקות את `document.body.dir` ולא את `documentElement`.
|
||
2. **CSS מפורש:** `html, body { direction: rtl; }` ב-base layer של Tailwind.
|
||
3. **DirectionProvider:** עטוף את ה-tree של React ב-`<DirectionProvider dir="rtl">` של `@radix-ui/react-direction`. בלי זה, רכיבי Radix (Tabs, Dialog, Select, Popover) ברירת המחדל שלהם היא LTR.
|
||
4. **Tabs wrapper:** עטוף את `TabsPrimitive.Root` עם default `dir="rtl"` כדי שגם אם DirectionProvider נמחק בעתיד, Tabs יישאר RTL.
|
||
5. **Tables:** `<table dir="rtl">` מפורש על כל טבלה — מבטיח סדר עמודות נכון בכל הדפדפנים.
|
||
6. **Logical classes:** השתמש ב-`text-start`/`text-end` (לא `text-left`/`text-right`), `ms-*`/`me-*` (לא `ml-*`/`mr-*`), `start-*`/`end-*`. אבל זכור: ב-RTL, **`text-end` מצמיד לשמאל**, לא לימין.
|
||
7. **בדיקה ויזואלית בדפדפן** לפני הצהרה ש-task UI הסתיים. build מוצלח לא מספיק.
|
||
|
||
---
|
||
|
||
## רישוי
|
||
|
||
MIT — ראה `LICENSE`.
|
||
|
||
---
|
||
|
||
## קישורים
|
||
|
||
- 🌐 **Web app:** https://nadlan-mcp.dev.marcus-law.co.il
|
||
- 📦 **Repo:** https://gitea.dev.marcus-law.co.il/mcp-servers/nadlan-mcp
|
||
- 📚 **API docs (Swagger):** https://nadlan-mcp.dev.marcus-law.co.il/api/docs
|
||
- 🛠 **MCP endpoint:** https://nadlan-mcp.dev.marcus-law.co.il/mcp
|
||
- 📖 **תיעוד אדריכלי:** `ARCHITECTURE.md`
|
||
- 🚀 **תיעוד פריסה:** `DEPLOYMENT.md`
|
||
- 📝 **שינויים:** `CHANGELOG.md`
|