# 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/`
