Property analysis, featuring AI-powered analysis via DeepAgent with a human-in-the-loop review workflow.
Users submit a free-form natural language query (e.g. "Analyse property price trends in GU1 over the last 3 years"). The agent parses the query, decides which skills to use, and executes them as a background task. Trace steps are streamed in real-time via Server-Sent Events (SSE). Once complete, the result enters a pending_review state where a human must manually accept or reject the suggestion before it is considered final.
# Install dependencies
cd server && uv sync
# Run the server (with PYTHONPATH for editable-installed package)
PYTHONPATH=$(pwd)/.. uv run python -m server.mainThis project uses Ruff for both linting and formatting.
# Check for lint errors
ruff check
# Auto-fix lint errors
ruff check --fix
# Format code
ruff formatConfiguration is in pyproject.toml under the [tool.ruff] section.
The API will be available at http://localhost:8000.
Interactive API documentation at:
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc
Create a .env file in the project root to override defaults:
ENVIRONMENT=development
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_MODEL=gemma4
LOG_LEVEL=INFOFor production:
ENVIRONMENT=production
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...# Run tests
pytest server/tests/ -vThis project uses Black as its code formatter. All Python files are formatted with the configuration defined in pyproject.toml (line length of 88, targeting Python 3.14).
# Format all Python files
black server/
# Check formatting without making changes (useful for CI)
black --check server/The project includes an MCP (Model Context Protocol) server that exposes the property analysis backend as tools for AI assistants like Cline.
AI Assistant (Cline)
↕ MCP (stdio transport)
property-analysis MCP server ──HTTP──► FastAPI (/api/v1/properties/*)
├─ trigger_analysis()
├─ get_analysis()
└─ review_analysis()
| Tool | Description |
|---|---|
trigger_analysis(query, additional_context?) |
Submit a natural-language property analysis request. Returns immediately with an analysis_id and status: "running". Poll with get_analysis for completion. |
get_analysis(analysis_id) |
Retrieve the analysis result by UUID. The response includes the current status (running, pending_review, accepted, rejected, or failed) and, once complete, the result containing the research note and chart data. |
review_analysis(analysis_id, action, reason?) |
Accept or reject a completed analysis. action must be "accept" or "reject". Requires the analysis to be in pending_review status. |
The MCP server is a lightweight Python process that communicates with the FastAPI backend over HTTP. It uses stdio transport — the AI assistant spawns the script as a subprocess and communicates over stdin/stdout.
server/
├── main.py # FastAPI entry point (uvicorn + --mcp flag)
├── core/
│ └── config.py # FASTAPI_URL setting (env-configurable)
└── mcp_server/
├── __init__.py
└── server.py # FastMCP server (3 tools, httpx client)
The MCP server is registered in cline_mcp_settings.json and auto-started by Cline. To run manually:
# Start the FastAPI server first (required)
cd server && PYTHONPATH=$(pwd)/.. uv run python -m server.main
# In another terminal, start the MCP server
cd server && PYTHONPATH=$(pwd)/.. uv run python -m server.mcp_server.serverAlternatively, use the --mcp flag on the main entry point:
cd server && PYTHONPATH=$(pwd)/.. uv run python -m server.main --mcpThe FastAPI base URL is configurable via the FASTAPI_URL setting in server/core/config.py (or the .env file):
# .env — overrides the default for development or production
FASTAPI_URL=http://localhost:8000- Default:
http://localhost:8000(local development) - Production: Set
FASTAPI_URL=https://api.yourdomain.comin.env
The MCP uses PYTHONPATH environment variable to resolve the editable-installed server package. This is configured in cline_mcp_settings.json under the property-analysis entry.
- Development: Uses Ollama to develop and test the changes to cut down the cost.
- Productions - Uses OPENAI primary, fallback is set to Anthropic. Easily switchable.
- Responsibility: Manages external interactions, analysis lifecycle, and real-time event streaming.
- Key Features: Background task processing for long-running analyses and Server-Sent Events (SSE) for live trace updates.
The "brain" of the system, implementing a Plan-Act-Evaluate loop.
- Planning Phase: Uses an LLM to map a query to a sequence of "Skills" based on YAML metadata.
- Execution Phase: Executes the plan. Uses Async Execution to handle I/O-bound data fetching without blocking the server.
- Evaluation Phase: A second LLM pass determines if the gathered data is sufficient to answer the query. If not, it triggers a re-planning loop.
- Synthesis Phase: Final synthesis of research notes and structured chart data.
- Modularity: Skills are defined in YAML files (
server/api/properties/skills/), decoupling metadata (description, inputs) from implementation. - Dynamic Loading: The
skills_loaderhandles lazy-loading of Python functions, allowing the system to scale its capabilities without modifying the core agent logic. - Async Implementation: Skills leverage
httpxfor non-blocking network calls to Land Registry SPARQL and REST endpoints.
- Analysis Store: Manages the state of analysis objects (Pending, Running, Completed, Reviewed).
- Storage: Currently uses a file-based JSON store for simplicity and local portability.
- Choice: LLM-driven planning.
- Trade-off:
- Pros: Extremely flexible; can handle unexpected query variations and combine data in novel ways.
- Cons: Non-deterministic; occasionally requires re-planning loops, increasing latency and token costs compared to a hard-coded decision tree.
- Choice: Skill-based modularity via YAML definitions.
- Trade-off:
- Pros: New data sources can be added by simply dropping a YAML and a Python function. The LLM "learns" the new tool automatically from its description.
- Cons: Adds an abstraction layer (loader/meta-registry) that must be maintained and kept in sync with the actual code signatures.
- Choice: Full Async Execution
- Trade-off:
- Pros: Allows the system to handle many concurrent analyses and fetch data from multiple endpoints efficiently. Essential for the SSE streaming UX.
- Cons: Increases complexity in error handling and requires all downstream libraries (like
httpx) to be async-compatible.
- Choice: Plan-Act-Evaluate Pattern.
- Trade-off:
- Pros: Predictability & Control is High; Can inspect, modify, or block the plan before any action is taken. Perfect for human-in-the-loop validation.
- Cons: High Initial Latency. Significant upfront delay while the plan is generated, but actual execution is often faster and can sometimes be parallelized.
- Choice: Async streaming.
- Trade-off:
- Pros: Excellent UX; Users see each agent step as it happens (skill execution, LLM evaluation decisions, errors) rather than waiting for a single opaque response.
- Cons: Infrastructure complexity. Requires an in-memory pub/sub event manager (
SSEEventManager) with per-analysis subscriber queues
- Choice: Server Sent Events(SSE).
- Trade-off:
- Pros: Real-time UX; streams trace step updates in real-time as the agent executes each step. The client receives events progressively.
- Cons: Comparatively complex implementation.
- Choice: YAML skills.
- Trade-off:
- Pros: Routing, metadata parsing, constraints, strictly structured templates.
- Cons: Less flexible. Syntax Strictness is High. A single misplaced indentation space breaks the agent's startup.
- Choice: File-based JSON (
analyses.json). - Trade-off:
- Pros: Zero configuration; perfect for a prototype or local tool. Easy to inspect and debug.
- Cons: Not suitable for high-concurrency environments; lacks row-level locking and advanced querying capabilities.