Create/update all Sprint 6 documentation: - CHANGELOG.md: document GOO-33 and recent audit findings - CONTRIBUTING.md: add branching, PR, commit conventions - docs/ci-cd.md: GitHub Actions pipeline documentation - docs/onboarding.md: developer setup & onboarding guide - docs/mcp-servers.md: MCP servers API documentation - docs/PROJECT_TRACKER.md: mark GOO-33 as in_progress - docs/QA_TRACKER.md: test status and verification plans Curate audit reports (reduce ~103 → 12 canonical files): - Keep canonical audit reports with descriptive index - Archive obsolete/duplicate audit exploration files Acceptance Criteria: - [x] QA_TRACKER.md exists with current test status - [x] CHANGELOG.md updated to today - [x] PROJECT_TRACKER.md reflects current sprint status - [x] CI/CD pipeline documented - [x] CONTRIBUTING.md has branching, PR, commit conventions - [x] docs/audits/ reduced to canonical reports Co-Authored-By: Paperclip <noreply@paperclip.ing>
601 lines
12 KiB
Markdown
601 lines
12 KiB
Markdown
# MCP Servers Documentation
|
|
|
|
GoodGo Platform sử dụng **Model Context Protocol (MCP)** để cấp quyền truy cập các công cụ chuyên dụng cho Claude AI. Ba MCP servers chính cung cấp khả năng **tìm kiếm bất động sản**, **phân tích thị trường**, và **định giá tự động (AVM)**.
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
| Server | Port | Tools | Purpose |
|
|
|--------|------|-------|---------|
|
|
| **Property Search** | 3001 | `search_properties`, `compare_properties`, `get_property_details` | Tìm kiếm bất động sản, so sánh, chi tiết |
|
|
| **Market Analytics** | 3001 | `get_market_report`, `analyze_trends`, `get_price_indices` | Phân tích thị trường, xu hướng giá |
|
|
| **Valuation** | 3001 | `estimate_valuation`, `extract_features`, `compare_valuations` | Định giá BĐS, so sánh XGBoost |
|
|
|
|
**Architecture:**
|
|
- All MCP servers are **in-process** (không tách microservice cho MVP)
|
|
- Chạy cùng process NestJS API
|
|
- HTTP transport dùng `/mcp/tools/*` endpoint
|
|
- Claude API calls `POST /mcp/tools/{toolName}` để invoke tools
|
|
|
|
---
|
|
|
|
## 1. Property Search Server
|
|
|
|
### Purpose
|
|
|
|
Cung cấp công cụ tìm kiếm bất động sản với:
|
|
- Full-text search (Typesense)
|
|
- Geo-spatial search (PostGIS)
|
|
- Faceted filtering (giá, loại, phòng ngủ, quận huyện)
|
|
|
|
### Tools
|
|
|
|
#### `search_properties`
|
|
|
|
Tìm kiếm bất động sản với nhiều tiêu chí.
|
|
|
|
**Input Schema:**
|
|
```json
|
|
{
|
|
"query": "chung cư quận 1",
|
|
"filters": {
|
|
"priceMin": 1000000000,
|
|
"priceMax": 5000000000,
|
|
"bedrooms": 2,
|
|
"district": "Quận 1",
|
|
"propertyType": "APARTMENT"
|
|
},
|
|
"sort": "price_asc",
|
|
"limit": 10,
|
|
"offset": 0
|
|
}
|
|
```
|
|
|
|
**Parameters:**
|
|
| Param | Type | Required | Default | Notes |
|
|
|-------|------|----------|---------|-------|
|
|
| `query` | string | Yes | - | Tìm kiếm từ khóa (tiêu đề, mô tả) |
|
|
| `filters.priceMin` | number | No | 0 | Giá tối thiểu (VND) |
|
|
| `filters.priceMax` | number | No | ∞ | Giá tối đa (VND) |
|
|
| `filters.bedrooms` | number | No | - | Số phòng ngủ |
|
|
| `filters.district` | string | No | - | Quận huyện |
|
|
| `filters.propertyType` | enum | No | - | APARTMENT, HOUSE, LAND, ROOM_RENTAL |
|
|
| `sort` | enum | No | `relevance` | `price_asc`, `price_desc`, `newest`, `relevance` |
|
|
| `limit` | number | No | 20 | Số kết quả (1-100) |
|
|
| `offset` | number | No | 0 | Phân trang offset |
|
|
|
|
**Output Example:**
|
|
```json
|
|
{
|
|
"total": 245,
|
|
"results": [
|
|
{
|
|
"id": "prop-001",
|
|
"title": "Căn hộ 2PN view sông Sài Gòn",
|
|
"type": "APARTMENT",
|
|
"price": 3500000000,
|
|
"bedrooms": 2,
|
|
"bathrooms": 2,
|
|
"area": 85,
|
|
"district": "Quận 1",
|
|
"ward": "Phường Bến Nghé",
|
|
"address": "123 Tôn Đức Thắng, Q.1",
|
|
"agentName": "Nguyễn Văn A",
|
|
"agentPhone": "0987654321",
|
|
"status": "ACTIVE",
|
|
"createdAt": "2026-04-20T10:30:00Z"
|
|
}
|
|
],
|
|
"facets": {
|
|
"districts": [
|
|
{ "name": "Quận 1", "count": 42 },
|
|
{ "name": "Quận 3", "count": 38 }
|
|
],
|
|
"propertyTypes": [
|
|
{ "name": "APARTMENT", "count": 120 },
|
|
{ "name": "HOUSE", "count": 85 }
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `compare_properties`
|
|
|
|
So sánh 2-5 bất động sản dựa trên giá, diện tích, vị trí, etc.
|
|
|
|
**Input Schema:**
|
|
```json
|
|
{
|
|
"propertyIds": ["prop-001", "prop-002", "prop-003"],
|
|
"metrics": ["price", "area", "price_per_sqm", "location_score", "agent_rating"]
|
|
}
|
|
```
|
|
|
|
**Output Example:**
|
|
```json
|
|
{
|
|
"comparison": [
|
|
{
|
|
"propertyId": "prop-001",
|
|
"title": "Căn hộ 2PN Q.1",
|
|
"price": 3500000000,
|
|
"area": 85,
|
|
"price_per_sqm": 41176470,
|
|
"location_score": 4.8,
|
|
"agent_rating": 4.5
|
|
},
|
|
{
|
|
"propertyId": "prop-002",
|
|
"title": "Căn hộ 2PN Q.3",
|
|
"price": 2800000000,
|
|
"area": 78,
|
|
"price_per_sqm": 35897436,
|
|
"location_score": 4.3,
|
|
"agent_rating": 4.2
|
|
}
|
|
],
|
|
"recommendation": "prop-002 có giá tốt hơn với vị trí tương đương"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `get_property_details`
|
|
|
|
Lấy chi tiết đầy đủ của một bất động sản.
|
|
|
|
**Input Schema:**
|
|
```json
|
|
{
|
|
"propertyId": "prop-001"
|
|
}
|
|
```
|
|
|
|
**Output Example:**
|
|
```json
|
|
{
|
|
"id": "prop-001",
|
|
"title": "Căn hộ 2PN view sông Sài Gòn",
|
|
"description": "Căn hộ cao cấp tại trung tâm Q.1...",
|
|
"type": "APARTMENT",
|
|
"price": 3500000000,
|
|
"bedrooms": 2,
|
|
"bathrooms": 2,
|
|
"area": 85,
|
|
"district": "Quận 1",
|
|
"ward": "Phường Bến Nghé",
|
|
"address": "123 Tôn Đức Thắng, Q.1",
|
|
"coordinates": {
|
|
"latitude": 10.7769,
|
|
"longitude": 106.7009
|
|
},
|
|
"features": ["view sông", "ban công rộng", "tầng cao", "gần metro"],
|
|
"agent": {
|
|
"id": "agent-001",
|
|
"name": "Nguyễn Văn A",
|
|
"phone": "0987654321",
|
|
"rating": 4.5,
|
|
"reviews": 47
|
|
},
|
|
"media": [
|
|
{
|
|
"type": "image",
|
|
"url": "https://storage.goodgo.app/prop-001/img-1.jpg"
|
|
}
|
|
],
|
|
"status": "ACTIVE",
|
|
"createdAt": "2026-04-20T10:30:00Z",
|
|
"reviews": [
|
|
{
|
|
"author": "Trần Văn B",
|
|
"rating": 5,
|
|
"text": "Căn hộ đẹp, vị trí tuyệt vời"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Market Analytics Server
|
|
|
|
### Purpose
|
|
|
|
Cung cấp dữ liệu thị trường bất động sản:
|
|
- Báo cáo giá theo quận huyện
|
|
- Phân tích xu hướng giá theo thời gian
|
|
- Chỉ số thị trường
|
|
|
|
### Tools
|
|
|
|
#### `get_market_report`
|
|
|
|
Lấy báo cáo thị trường theo quận huyện.
|
|
|
|
**Input Schema:**
|
|
```json
|
|
{
|
|
"district": "Quận 1",
|
|
"propertyType": "APARTMENT",
|
|
"period": "monthly"
|
|
}
|
|
```
|
|
|
|
**Output Example:**
|
|
```json
|
|
{
|
|
"district": "Quận 1",
|
|
"propertyType": "APARTMENT",
|
|
"period": "April 2026",
|
|
"statistics": {
|
|
"averagePrice": 3200000000,
|
|
"medianPrice": 3100000000,
|
|
"priceMin": 800000000,
|
|
"priceMax": 8500000000,
|
|
"averageArea": 82,
|
|
"averagePricePerSqm": 39024390,
|
|
"activeListings": 342,
|
|
"soldListings": 28,
|
|
"rentedListings": 15
|
|
},
|
|
"trends": {
|
|
"priceChangePercent": 2.5,
|
|
"priceChangeVsLastMonth": "Tăng 2.5%",
|
|
"velocityDays": 15
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `analyze_trends`
|
|
|
|
Phân tích xu hướng giá theo thời gian.
|
|
|
|
**Input Schema:**
|
|
```json
|
|
{
|
|
"district": "Quận 1",
|
|
"propertyType": "APARTMENT",
|
|
"months": 12
|
|
}
|
|
```
|
|
|
|
**Output Example:**
|
|
```json
|
|
{
|
|
"district": "Quận 1",
|
|
"propertyType": "APARTMENT",
|
|
"trend": [
|
|
{
|
|
"month": "May 2025",
|
|
"averagePrice": 2900000000,
|
|
"medianPrice": 2800000000,
|
|
"activeListings": 250
|
|
},
|
|
{
|
|
"month": "April 2026",
|
|
"averagePrice": 3200000000,
|
|
"medianPrice": 3100000000,
|
|
"activeListings": 342
|
|
}
|
|
],
|
|
"forecast": {
|
|
"nextMonthPredicted": 3280000000,
|
|
"confidence": 0.75,
|
|
"direction": "up"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `get_price_indices`
|
|
|
|
Lấy chỉ số giá toàn thị trường (bình thường hóa = 100).
|
|
|
|
**Input Schema:**
|
|
```json
|
|
{
|
|
"baseMonth": "January 2025"
|
|
}
|
|
```
|
|
|
|
**Output Example:**
|
|
```json
|
|
{
|
|
"baseIndex": 100,
|
|
"baseMonth": "January 2025",
|
|
"indices": [
|
|
{
|
|
"month": "January 2025",
|
|
"indexValue": 100,
|
|
"growth": 0
|
|
},
|
|
{
|
|
"month": "April 2026",
|
|
"indexValue": 110.5,
|
|
"growth": 10.5
|
|
}
|
|
],
|
|
"byDistrict": {
|
|
"Quận 1": 112.3,
|
|
"Quận 3": 108.7,
|
|
"Thủ Đức": 105.2
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Valuation Server
|
|
|
|
### Purpose
|
|
|
|
Định giá tự động bất động sản dùng mô hình **XGBoost**:
|
|
- Estimate valuation based on features
|
|
- Extract features from description
|
|
- Compare valuations across similar properties
|
|
|
|
### Tools
|
|
|
|
#### `estimate_valuation`
|
|
|
|
Ước lượng giá bất động sản dựa trên đặc trưng.
|
|
|
|
**Input Schema:**
|
|
```json
|
|
{
|
|
"district": "Quận 1",
|
|
"propertyType": "APARTMENT",
|
|
"area": 85,
|
|
"bedrooms": 2,
|
|
"bathrooms": 2,
|
|
"features": ["view sông", "ban công", "tầng cao", "gần metro"],
|
|
"yearBuilt": 2015,
|
|
"location": {
|
|
"latitude": 10.7769,
|
|
"longitude": 106.7009
|
|
}
|
|
}
|
|
```
|
|
|
|
**Output Example:**
|
|
```json
|
|
{
|
|
"estimatedPrice": 3250000000,
|
|
"priceRange": {
|
|
"low": 2850000000,
|
|
"high": 3650000000
|
|
},
|
|
"confidence": 0.82,
|
|
"factors": {
|
|
"area": "Positive (85 sqm)",
|
|
"location": "High demand (Q.1)",
|
|
"features": "Premium (view sông, gần metro)",
|
|
"yearBuilt": "Neutral (11 years old)"
|
|
},
|
|
"comparables": [
|
|
{
|
|
"id": "prop-001",
|
|
"actualPrice": 3200000000,
|
|
"similarity": 0.89
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `extract_features`
|
|
|
|
Trích xuất đặc trưng từ mô tả bất động sản (NLP).
|
|
|
|
**Input Schema:**
|
|
```json
|
|
{
|
|
"description": "Căn hộ cao cấp 2 phòng ngủ, 2 phòng tắm, view sông Sài Gòn, ban công rộng, gần trạm metro, tầng 15, xây năm 2015...",
|
|
"title": "Căn hộ 2PN view sông Sài Gòn"
|
|
}
|
|
```
|
|
|
|
**Output Example:**
|
|
```json
|
|
{
|
|
"extracted": {
|
|
"bedrooms": 2,
|
|
"bathrooms": 2,
|
|
"area": null,
|
|
"features": ["view sông", "ban công", "gần metro", "tầng cao"],
|
|
"yearBuilt": 2015,
|
|
"condition": "tốt"
|
|
},
|
|
"confidence": {
|
|
"bedrooms": 0.98,
|
|
"features": 0.85,
|
|
"yearBuilt": 0.92
|
|
},
|
|
"uncertainties": [
|
|
"area not mentioned",
|
|
"exact floor number not in description"
|
|
]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### `compare_valuations`
|
|
|
|
So sánh định giá của các bất động sản tương tự.
|
|
|
|
**Input Schema:**
|
|
```json
|
|
{
|
|
"referencePropertyId": "prop-001",
|
|
"candidatePropertyIds": ["prop-002", "prop-003", "prop-004"]
|
|
}
|
|
```
|
|
|
|
**Output Example:**
|
|
```json
|
|
{
|
|
"reference": {
|
|
"propertyId": "prop-001",
|
|
"title": "Căn hộ 2PN Q.1",
|
|
"actualPrice": 3500000000,
|
|
"estimatedPrice": 3250000000
|
|
},
|
|
"candidates": [
|
|
{
|
|
"propertyId": "prop-002",
|
|
"title": "Căn hộ 2PN Q.3",
|
|
"actualPrice": 2800000000,
|
|
"estimatedPrice": 2950000000,
|
|
"overUndervalued": "Undervalued by 5.1%",
|
|
"similarity": 0.76
|
|
}
|
|
],
|
|
"recommendation": "prop-002 is a good value compared to reference property"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Implementation Details
|
|
|
|
### File Structure
|
|
|
|
```
|
|
apps/api/src/
|
|
├── modules/
|
|
│ └── mcp/
|
|
│ ├── mcp.module.ts # Module definition
|
|
│ ├── mcp.controller.ts # HTTP endpoint: POST /mcp/tools/:toolName
|
|
│ ├── mcp-registry.service.ts # Registry: tool name → handler function
|
|
│ ├── servers/
|
|
│ │ ├── property-search.server.ts
|
|
│ │ ├── market-analytics.server.ts
|
|
│ │ └── valuation.server.ts
|
|
│ └── dto/
|
|
│ ├── search-properties.dto.ts
|
|
│ ├── market-report.dto.ts
|
|
│ └── estimate-valuation.dto.ts
|
|
```
|
|
|
|
### McpRegistryService
|
|
|
|
```typescript
|
|
// Register tools
|
|
export class McpRegistryService {
|
|
private tools = new Map<string, Tool>();
|
|
|
|
register(name: string, tool: Tool) {
|
|
this.tools.set(name, tool);
|
|
}
|
|
|
|
invoke(name: string, input: any): Promise<any> {
|
|
const tool = this.tools.get(name);
|
|
if (!tool) throw new NotFoundException(`Tool ${name} not found`);
|
|
return tool.execute(input);
|
|
}
|
|
}
|
|
```
|
|
|
|
### HTTP Endpoint
|
|
|
|
```typescript
|
|
@Post('/mcp/tools/:toolName')
|
|
@Auth() // JWT required
|
|
async invokeTool(
|
|
@Param('toolName') toolName: string,
|
|
@Body() input: any
|
|
) {
|
|
return this.mcpRegistry.invoke(toolName, input);
|
|
}
|
|
```
|
|
|
|
### Tool Pattern
|
|
|
|
```typescript
|
|
interface Tool {
|
|
name: string;
|
|
schema: JsonSchema; // Input validation
|
|
execute(input: any): Promise<any>;
|
|
}
|
|
|
|
class SearchPropertiesTool implements Tool {
|
|
name = 'search_properties';
|
|
schema = { /* JSON Schema */ };
|
|
|
|
execute(input: SearchPropertiesInput): Promise<any> {
|
|
// Implementation
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
All MCP tools return consistent error format:
|
|
|
|
```json
|
|
{
|
|
"error": {
|
|
"code": "INVALID_INPUT",
|
|
"message": "District 'Quận XYZ' not found",
|
|
"details": {
|
|
"field": "district",
|
|
"value": "Quận XYZ"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Considerations
|
|
|
|
### Caching
|
|
|
|
- Market Analytics: Cache reports for 1 hour
|
|
- Property Search: Cache facets, invalidate on new listing
|
|
- Valuation: Cache model predictions for 24 hours
|
|
|
|
### Rate Limiting
|
|
|
|
- Default: 100 req/minute per user
|
|
- MCP tools: 50 req/minute per tool (stricter)
|
|
- Burst: 10 req/second
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Unit Tests
|
|
|
|
```bash
|
|
pnpm test -- mcp/servers
|
|
```
|
|
|
|
### Integration Tests
|
|
|
|
```bash
|
|
# Test via HTTP
|
|
curl -X POST http://localhost:3001/mcp/tools/search_properties \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"query": "chung cu quan 1", "limit": 5}'
|
|
```
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- MCP Specification: https://modelcontextprotocol.io/
|
|
- Claude API: https://anthropic.com/api
|
|
- Implementation: `apps/api/src/modules/mcp/`
|