# Bài 7: Kết nối mọi thứ lại với nhau

> *Khoảnh khắc "It works!" — Khi AI, MCP Server và Revit cùng hoạt động như một hệ thống thống nhất.*
>
> **Tham chiếu triển khai Tool/MCP Server:** [revit-mcp-server](https://github.com/nguyenngocdue/revit-mcp-server)

***

## Mục lục

* [Giới thiệu bài học](#giới-thiệu-bài-học)
* [Sơ đồ tổng quan hệ thống](#sơ-đồ-tổng-quan-hệ-thống)
* [Thứ tự khởi động hệ thống](#thứ-tự-khởi-động-hệ-thống)
* [Kiểm tra MCP connection](#kiểm-tra-mcp-connection)
* [Test 1: Lấy danh sách tường (get\_walls)](#test-1-lấy-danh-sách-tường-get_walls)
* [Test 2: Tạo tường mới (create\_wall)](#test-2-tạo-tường-mới-create_wall)
* [Test 3: Prompt phức tạp (multi-tool)](#test-3-prompt-phức-tạp-multi-tool)
* [Debug: Xem logs và kiểm tra connections](#debug-xem-logs-và-kiểm-tra-connections)
* [Xử lý lỗi thường gặp khi kết nối](#xử-lý-lỗi-thường-gặp-khi-kết-nối)
* [Sơ đồ hoạt động toàn trình](#sơ-đồ-hoạt-động-toàn-trình)
* [Câu hỏi tự suy nghĩ](#câu-hỏi-tự-suy-nghĩ)
* [Tổng kết](#tổng-kết)
* [Tài liệu tham khảo](#tài-liệu-tham-khảo)

***

## Giới thiệu bài học

Trong 6 bài trước, chúng ta đã xây dựng từng thành phần riêng lẻ:

| Bài   | Thành phần                     | Trạng thái |
| ----- | ------------------------------ | ---------- |
| Bài 3 | Revit API Tools (C#)           | Đã code    |
| Bài 4 | MCP Server (Node.js)           | Đã dựng    |
| Bài 5 | Core MCP Plugin (C#)           | Đã viết    |
| Bài 6 | Cấu hình VS Code / Claude Code | Đã setup   |

Bây giờ là lúc **kết nối tất cả lại với nhau** và chạy thử end-to-end. Đây là bài học quan trọng nhất của khóa học — nơi bạn sẽ thấy AI thực sự điều khiển được Revit thông qua ngôn ngữ tự nhiên.

> **Mục tiêu bài học:**
>
> * Khởi động đúng thứ tự toàn bộ hệ thống
> * Xác nhận AI nhìn thấy và gọi được các tool
> * Chạy thành công luồng: Prompt → AI → MCP → Revit → Kết quả
> * Biết cách debug khi có lỗi xảy ra

### Chuẩn kết nối áp dụng trong bài này: STDIO

Bài 7 sử dụng đúng chuẩn triển khai của khóa học: **AI Host ↔ MCP Server qua STDIO**.\
MCP Server sau đó gọi Revit Plugin qua `HTTP localhost:8080`.

![Luồng STDIO tập trung của khóa học](/files/a9HAMBPmTHprtmS3yGPs)

```json
{
  "mcpServers": {
    "revit-mcp": {
      "command": "node",
      "args": ["./mcp-server/dist/index.js"],
      "env": {
        "REVIT_API_URL": "http://localhost:8080"
      }
    }
  }
}
```

***

## Sơ đồ tổng quan hệ thống

Trước khi bắt đầu, hãy nhìn lại toàn bộ kiến trúc mà chúng ta sẽ kết nối:

```mermaid
graph LR
    A[Người dùng] -->|Nhập prompt| B[VS Code / Claude Code]
    B -->|MCP Protocol<br/>stdio| C[MCP Server<br/>Node.js]
    C -->|HTTP REST<br/>localhost:8080| D[Revit Plugin<br/>C#]
    D -->|Revit API| E[Autodesk Revit<br/>BIM Model]
    E -->|Dữ liệu BIM| D
    D -->|JSON Response| C
    C -->|Tool Result| B
    B -->|Hiển thị kết quả| A

    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style C fill:#e8f5e9
    style D fill:#fff3e0
    style E fill:#fce4ec
```

Mỗi thành phần có một nhiệm vụ rõ ràng và chúng giao tiếp với nhau theo một chiều nhất định. Nếu bất kỳ liên kết nào bị đứt, toàn bộ hệ thống sẽ không hoạt động.

***

## Thứ tự khởi động hệ thống

Thứ tự khởi động là **cực kỳ quan trọng**. Nếu bạn khởi động sai thứ tự, các thành phần sẽ không thể tìm thấy nhau.

### Sơ đồ trình tự khởi động

```mermaid
sequenceDiagram
    participant User as Người dùng
    participant Revit as Autodesk Revit
    participant Plugin as Revit Plugin (C#)
    participant MCP as MCP Server (Node.js)
    participant IDE as VS Code / Claude Code

    Note over User,IDE: === BƯỚC 1: Khởi động Revit ===
    User->>Revit: Mở Revit, load project
    Revit->>Plugin: Auto-load plugin (.addin)
    Plugin->>Plugin: Khởi tạo HTTP Listener<br/>port 8080
    Plugin-->>Revit: Plugin sẵn sàng

    Note over User,IDE: === BƯỚC 2: Kiểm tra Plugin ===
    User->>Plugin: Truy cập http://localhost:8080/health
    Plugin-->>User: {"status": "ok"}

    Note over User,IDE: === BƯỚC 3: Khởi động MCP Server ===
    User->>MCP: npm start hoặc node dist/index.js
    MCP->>MCP: Đăng ký tools:<br/>get_walls, create_wall, ...
    MCP-->>User: MCP Server running...

    Note over User,IDE: === BƯỚC 4: Kết nối từ IDE ===
    User->>IDE: Mở VS Code / Claude Code
    IDE->>MCP: Kết nối qua stdio
    MCP-->>IDE: Gửi danh sách tools
    IDE-->>User: MCP Connected!
```

### Bước 1: Khởi động Revit và load Plugin

Mở Autodesk Revit và chờ đến khi plugin được load tự động. Kiểm tra trong Revit console hoặc tab Add-ins:

```
[RevitMCP] Plugin loaded successfully
[RevitMCP] HTTP Listener started on port 8080
[RevitMCP] Waiting for connections...
```

Nếu plugin không tự động load, kiểm tra file `.addin` đã được đặt đúng vị trí:

```
%AppData%\Autodesk\Revit\Addins\2024\RevitMCP.addin
```

### Bước 2: Kiểm tra Plugin đã sẵn sàng

Mở trình duyệt hoặc dùng `curl` để kiểm tra:

```bash
# Kiểm tra Revit Plugin có đang lắng nghe không
curl http://localhost:8080/health

# Kết quả mong đợi:
# {"status": "ok", "revitVersion": "2024", "tools": ["get_walls", "create_wall"]}
```

Nếu không nhận được phản hồi, có thể do:

* Plugin chưa load thành công
* Port 8080 bị chiếm bởi ứng dụng khác
* Firewall chặn kết nối localhost

### Bước 3: Khởi động MCP Server

```bash
# Di chuyển đến thư mục MCP Server
cd mcp-server

# Cài đặt dependencies (lần đầu)
npm install

# Build TypeScript (nếu cần)
npm run build

# Khởi động server
npm start
```

Bạn sẽ thấy output như sau:

```
[MCP] Revit MCP Server v1.0.0
[MCP] Registered tools: get_walls, create_wall, get_elements, delete_element
[MCP] Transport: stdio
[MCP] Ready to accept connections...
```

### Bước 4: Kết nối từ VS Code hoặc Claude Code

**Với VS Code (Copilot Chat):**

Đảm bảo file `.vscode/settings.json` đã được cấu hình:

```json
{
  "mcp": {
    "servers": {
      "revit-mcp": {
        "command": "node",
        "args": ["./mcp-server/dist/index.js"],
        "env": {
          "REVIT_API_URL": "http://localhost:8080"
        }
      }
    }
  }
}
```

**Với Claude Code:**

Đảm bảo file `.mcp.json` tại thư mục gốc dự án:

```json
{
  "mcpServers": {
    "revit-mcp": {
      "command": "node",
      "args": ["./mcp-server/dist/index.js"],
      "env": {
        "REVIT_API_URL": "http://localhost:8080"
      }
    }
  }
}
```

Sau đó khởi động Claude Code:

```bash
claude
```

***

## Kiểm tra MCP connection

Sau khi khởi động xong, việc đầu tiên là **xác nhận AI đã nhìn thấy các tool**.

### Lệnh `/mcp` trong Claude Code

```bash
# Trong giao diện Claude Code, gõ:
/mcp
```

Kết quả mong đợi:

```
MCP Servers:

  revit-mcp
    Status: connected
    Tools:
      - get_walls: Lấy danh sách tất cả tường trong Revit model
      - create_wall: Tạo một bức tường mới trong Revit
      - get_elements: Lấy danh sách elements theo category
      - delete_element: Xóa một element theo ID
```

Nếu trạng thái là `disconnected` hoặc không thấy tool nào, xem mục [Xử lý lỗi thường gặp khi kết nối](#xử-lý-lỗi-thường-gặp-khi-kết-nối).

### Sơ đồ trạng thái hệ thống

```mermaid
stateDiagram-v2
    [*] --> Disconnected

    Disconnected --> Connecting : Khởi động MCP Server
    Connecting --> Connected : Handshake thành công
    Connecting --> Error : Timeout / Port bị chiếm

    Connected --> Executing : Nhận tool call
    Executing --> Connected : Trả kết quả thành công
    Executing --> Error : Revit không phản hồi

    Error --> Connecting : Retry / Khởi động lại
    Error --> Disconnected : Dừng hệ thống

    Connected --> Disconnected : Tắt MCP Server
```

| Trạng thái       | Mô tả                       | Hành động                  |
| ---------------- | --------------------------- | -------------------------- |
| **Disconnected** | Các thành phần chưa kết nối | Khởi động theo đúng thứ tự |
| **Connecting**   | Đang thiết lập kết nối      | Chờ handshake hoàn tất     |
| **Connected**    | Hệ thống sẵn sàng           | Có thể gửi prompt          |
| **Executing**    | Đang xử lý tool call        | Chờ kết quả từ Revit       |
| **Error**        | Có lỗi xảy ra               | Xem logs, debug            |

***

## Test 1: Lấy danh sách tường (get\_walls)

Đây là test đơn giản nhất — chỉ đọc dữ liệu từ Revit, không thay đổi gì trong model.

### Prompt thực tế

```
Hãy liệt kê tất cả các bức tường trong model Revit hiện tại,
bao gồm tên, chiều dài và ID của từng tường.
```

### Quá trình xử lý

```mermaid
sequenceDiagram
    participant User as Người dùng
    participant AI as Claude AI
    participant MCP as MCP Server
    participant Plugin as Revit Plugin
    participant Revit as Revit API

    User->>AI: "Liệt kê tất cả tường trong model"
    Note over AI: Phân tích prompt...<br/>Cần dùng tool get_walls

    AI->>MCP: Tool call: get_walls()
    MCP->>Plugin: GET http://localhost:8080/api/get-walls
    Plugin->>Revit: FilteredElementCollector<br/>.OfClass(typeof(Wall))
    Revit-->>Plugin: List<Wall> walls
    Plugin-->>MCP: JSON response
    MCP-->>AI: Tool result (JSON)

    Note over AI: Định dạng kết quả<br/>thành câu trả lời

    AI-->>User: "Model hiện có 5 bức tường..."
```

### Kết quả mong đợi

AI sẽ trả về kết quả tương tự như:

```
Model hiện tại có 5 bức tường:

| # | ID    | Tên                          | Chiều dài (m) |
|---|-------|------------------------------|---------------|
| 1 | 12345 | Basic Wall: Generic - 200mm  | 10.0          |
| 2 | 12346 | Basic Wall: Generic - 200mm  | 5.0           |
| 3 | 12347 | Basic Wall: Generic - 300mm  | 10.0          |
| 4 | 12348 | Basic Wall: Generic - 300mm  | 5.0           |
| 5 | 12349 | Basic Wall: Exterior - 250mm | 15.0          |

Tổng chiều dài tường: 45.0 mét.
```

### Lý giải

* AI đọc prompt và nhận ra cần lấy thông tin tường → chọn tool `get_walls`
* MCP Server nhận tool call, chuyển thành HTTP request đến Revit Plugin
* Plugin sử dụng `FilteredElementCollector` của Revit API để truy vấn dữ liệu
* Dữ liệu trả về dưới dạng JSON, AI định dạng thành bảng để dễ đọc

***

## Test 2: Tạo tường mới (create\_wall)

Test này thay đổi model Revit — tạo một bức tường mới.

### Prompt thực tế

```
Tạo một bức tường dài 8 mét, cao 3 mét,
bắt đầu từ tọa độ (0, 0) đến (8, 0).
```

### Quá trình xử lý

```mermaid
sequenceDiagram
    participant User as Người dùng
    participant AI as Claude AI
    participant MCP as MCP Server
    participant Plugin as Revit Plugin
    participant Revit as Revit API

    User->>AI: "Tạo tường 8m x 3m từ (0,0) đến (8,0)"
    Note over AI: Phân tích prompt...<br/>Cần dùng tool create_wall<br/>với các tham số cụ thể

    AI->>MCP: Tool call: create_wall(<br/>startX=0, startY=0,<br/>endX=8, endY=0,<br/>height=3)
    MCP->>Plugin: POST http://localhost:8080/api/create-wall<br/>Body: {"startX":0,"startY":0,"endX":8,"endY":0,"height":3}

    Note over Plugin: Chuyển đổi mét -> feet<br/>Tạo ExternalEvent

    Plugin->>Revit: Wall.Create(doc, line, wallType, level, height)
    Revit-->>Plugin: Wall created (ID: 56789)
    Plugin-->>MCP: {"success": true, "wallId": 56789}
    MCP-->>AI: Tool result

    AI-->>User: "Đã tạo thành công bức tường ID 56789"
```

### Kết quả mong đợi

```
Đã tạo thành công bức tường mới trong Revit:

- ID: 56789
- Loại: Basic Wall: Generic - 200mm
- Điểm bắt đầu: (0, 0)
- Điểm kết thúc: (8, 0)
- Chiều dài: 8.0 mét
- Chiều cao: 3.0 mét

Bạn có thể kiểm tra trong Revit bằng cách chuyển sang 3D View.
```

### Những điều cần lưu ý

1. **Đơn vị đo**: Revit sử dụng **feet** nội bộ, nhưng người dùng và AI trao đổi bằng **mét**. Plugin cần tự động chuyển đổi.
2. **ExternalEvent**: Revit API yêu cầu mọi thay đổi model phải chạy trên **main thread** thông qua `ExternalEvent`. Nếu không, sẽ gặp lỗi `InvalidOperationException`.
3. **Transaction**: Mọi thay đổi model phải nằm trong một `Transaction`:

```csharp
using (Transaction trans = new Transaction(doc, "Create Wall"))
{
    trans.Start();
    Wall.Create(doc, line, wallTypeId, levelId, height, 0, false, false);
    trans.Commit();
}
```

***

## Test 3: Prompt phức tạp (multi-tool)

Đây là test quan trọng nhất — AI phải **sử dụng nhiều tool kết hợp** để hoàn thành yêu cầu.

### Prompt thực tế

```
Hãy làm các việc sau:
1. Liệt kê tất cả tường hiện có trong model
2. Tính tổng chiều dài tất cả tường
3. Tạo thêm 2 bức tường mới tạo thành hình chữ L:
   - Tường 1: từ (0, 10) đến (6, 10), cao 3m
   - Tường 2: từ (6, 10) đến (6, 16), cao 3m
4. Sau khi tạo xong, liệt kê lại tất cả tường để xác nhận
```

### Sơ đồ xử lý đa bước

```mermaid
graph TD
    A[Nhận prompt phức tạp] --> B[Bước 1: Gọi get_walls]
    B --> C[Nhận danh sách tường hiện có]
    C --> D[Bước 2: Tính tổng chiều dài]
    D --> E[Bước 3a: Gọi create_wall<br/>Tường 1: 0,10 -> 6,10]
    E --> F[Bước 3b: Gọi create_wall<br/>Tường 2: 6,10 -> 6,16]
    F --> G[Bước 4: Gọi get_walls lần 2]
    G --> H[Trả kết quả tổng hợp cho người dùng]

    style A fill:#e3f2fd
    style B fill:#e8f5e9
    style E fill:#fff3e0
    style F fill:#fff3e0
    style G fill:#e8f5e9
    style H fill:#f3e5f5
```

### Kết quả mong đợi

```
Tôi đã thực hiện đầy đủ các yêu cầu:

**1. Danh sách tường ban đầu (5 tường):**

| # | ID    | Tên                         | Chiều dài |
|---|-------|-----------------------------|-----------|
| 1 | 12345 | Basic Wall: Generic - 200mm | 10.0 m    |
| 2 | 12346 | Basic Wall: Generic - 200mm | 5.0 m     |
| 3 | 12347 | Basic Wall: Generic - 300mm | 10.0 m    |
| 4 | 12348 | Basic Wall: Generic - 300mm | 5.0 m     |
| 5 | 12349 | Basic Wall: Exterior        | 15.0 m    |

**2. Tổng chiều dài ban đầu:** 45.0 mét

**3. Đã tạo 2 tường mới:**
- Tường L-1 (ID: 56790): từ (0, 10) đến (6, 10) — 6.0 m
- Tường L-2 (ID: 56791): từ (6, 10) đến (6, 16) — 6.0 m

**4. Danh sách tường sau khi tạo (7 tường):**

| # | ID    | Tên                         | Chiều dài |
|---|-------|-----------------------------|-----------|
| 1 | 12345 | Basic Wall: Generic - 200mm | 10.0 m    |
| 2 | 12346 | Basic Wall: Generic - 200mm | 5.0 m     |
| 3 | 12347 | Basic Wall: Generic - 300mm | 10.0 m    |
| 4 | 12348 | Basic Wall: Generic - 300mm | 5.0 m     |
| 5 | 12349 | Basic Wall: Exterior        | 15.0 m    |
| 6 | 56790 | Basic Wall: Generic - 200mm | 6.0 m     |
| 7 | 56791 | Basic Wall: Generic - 200mm | 6.0 m     |

**Tổng chiều dài mới:** 57.0 mét (tăng 12.0 m)
```

### Lý giải quá trình multi-tool

Khi nhận prompt phức tạp, AI thực hiện **chuỗi suy luận** (chain of thought):

1. **Phân tích yêu cầu** — Tách prompt thành các bước nhỏ
2. **Lập kế hoạch** — Xác định cần gọi tool nào, theo thứ tự nào
3. **Thực thi tuần tự** — Gọi từng tool và chờ kết quả trước khi gọi tool tiếp theo
4. **Tổng hợp** — Gom kết quả từ nhiều tool thành câu trả lời hoàn chỉnh

Điểm đặc biệt là AI có thể **sử dụng kết quả của tool trước** để quyết định hành động tiếp theo. Ví dụ, nếu `get_walls` trả về lỗi, AI sẽ báo lỗi thay vì tiếp tục tạo tường.

***

## Debug: Xem logs và kiểm tra connections

Khi hệ thống không hoạt động như mong đợi, hãy debug theo trình tự sau.

### Sơ đồ quy trình debug

```mermaid
flowchart TD
    A[Lỗi xảy ra] --> B{MCP kết nối<br/>được không?}
    B -->|Không| C[Kiểm tra /mcp status]
    C --> D{MCP Server<br/>đang chạy?}
    D -->|Không| E[Chạy npm start]
    D -->|Có| F{Cấu hình<br/>.mcp.json đúng?}
    F -->|Sai| G[Sửa lại đường dẫn<br/>trong .mcp.json]
    F -->|Đúng| H[Kiểm tra logs<br/>MCP Server]

    B -->|Có| I{Tool call<br/>thành công?}
    I -->|Không| J{Revit Plugin<br/>phản hồi?}
    J -->|Không| K[Kiểm tra<br/>curl localhost:8080/health]
    K --> L{Phản hồi?}
    L -->|Không| M[Khởi động lại Revit<br/>Reload plugin]
    L -->|Có| N[Kiểm tra<br/>tool routing]

    J -->|Có| O[Kiểm tra<br/>Revit API error logs]

    I -->|Có| P[Hệ thống<br/>hoạt động bình thường]

    style A fill:#ffcdd2
    style P fill:#c8e6c9
    style E fill:#fff9c4
    style G fill:#fff9c4
    style M fill:#fff9c4
```

### Xem logs MCP Server (Node.js)

```bash
# Chạy với chế độ debug để xem log chi tiết
DEBUG=* npm start

# Hoặc thêm biến môi trường
MCP_LOG_LEVEL=debug npm start
```

Output mẫu khi hoạt động bình thường:

```
[MCP:debug] Server initialized
[MCP:debug] Transport: stdio connected
[MCP:debug] Tool call received: get_walls
[MCP:debug] Forwarding to http://localhost:8080/api/get-walls
[MCP:debug] Response received: 200 OK (342ms)
[MCP:debug] Returning 5 walls to AI
```

### Xem logs Revit Plugin (C#)

Kiểm tra file log tại:

```
%AppData%\RevitMCP\logs\revitmcp.log
```

Hoặc xem trong Revit Journal file:

```
%LocalAppData%\Autodesk\Revit\Autodesk Revit 2024\Journals\
```

Dòng log mẫu khi thành công:

```
[2024-01-15 10:23:45] [INFO] HTTP Listener started on port 8080
[2024-01-15 10:23:52] [INFO] Received request: GET /api/get-walls
[2024-01-15 10:23:52] [INFO] Found 5 walls in active document
[2024-01-15 10:23:52] [INFO] Response sent (342ms)
```

### Kiểm tra kết nối bằng curl

```bash
# Test 1: Kiểm tra Plugin có đang lắng nghe không
curl -v http://localhost:8080/health

# Test 2: Lấy danh sách tường trực tiếp
curl http://localhost:8080/api/get-walls

# Test 3: Tạo tường (POST request)
curl -X POST http://localhost:8080/api/create-wall \
  -H "Content-Type: application/json" \
  -d '{"startX":0,"startY":0,"endX":5,"endY":0,"height":3}'
```

***

## Xử lý lỗi thường gặp khi kết nối

### Lỗi 1: MCP Server không kết nối được

**Triệu chứng:** Lệnh `/mcp` hiển thị `disconnected`

**Nguyên nhân thường gặp:**

```bash
# Kiểm tra MCP Server có đang chạy không
ps aux | grep "mcp-server"

# Kiểm tra port có bị chiếm không
lsof -i :8080
# Hoặc trên Windows:
netstat -ano | findstr :8080
```

**Cách khắc phục:**

```bash
# Khởi động lại MCP Server
cd mcp-server
npm start

# Nếu port bị chiếm, đổi port trong config
# Sửa file mcp-server/src/config.ts:
# export const REVIT_API_URL = "http://localhost:8081"
```

### Lỗi 2: Tool call thất bại — Revit không phản hồi

**Triệu chứng:** AI báo "Tool call failed" hoặc timeout

**Kiểm tra:**

```bash
# Test trực tiếp HTTP đến Revit Plugin
curl -v http://localhost:8080/api/get-walls

# Kết quả tốt:
# HTTP/1.1 200 OK
# [{"Id":12345,"Name":"Basic Wall",...}]

# Kết quả lỗi:
# curl: (7) Failed to connect to localhost port 8080: Connection refused
```

**Nguyên nhân và cách khắc phục:**

| Nguyên nhân      | Cách khắc phục                        |
| ---------------- | ------------------------------------- |
| Plugin chưa load | Khởi động lại Revit, kiểm tra .addin  |
| Port bị chiếm    | Đổi port hoặc tắt ứng dụng chiếm port |
| Firewall chặn    | Cho phép port 8080 trong firewall     |
| Revit bị treo    | Tắt và mở lại Revit                   |

### Lỗi 3: Dữ liệu trả về rỗng hoặc sai

**Triệu chứng:** AI nói "Không có tường nào" trong khi model có tường

**Kiểm tra:**

```bash
# Test trực tiếp API
curl http://localhost:8080/api/get-walls | python -m json.tool

# Kiểm tra Revit đã mở đúng project chưa
# Plugin chỉ truy vấn document hiện tại
```

**Nguyên nhân thường gặp:**

* Revit đang mở project rỗng (không có model)
* Plugin đang trỏ đến sai Document
* Lỗi chuyển đổi dữ liệu JSON

### Lỗi 4: Transaction lỗi trong Revit

**Triệu chứng:** Tool `create_wall` thất bại với thông báo lỗi

**Kiểm tra log Revit:**

```
[RevitMCP] Error: Autodesk.Revit.Exceptions.InvalidOperationException
  Starting a new transaction is not permitted. It could be because another
  transaction already started and has not been completed yet.
```

**Cách khắc phục:**

* Đảm bảo mọi Transaction đã được `Commit()` hoặc `RollBack()`
* Không chạy Transaction lồng nhau
* Sử dụng `ExternalEvent` để đảm bảo chạy trên UI thread

### Lỗi 5: MCP Server báo "spawn node ENOENT"

**Triệu chứng:**

```
Error: spawn node ENOENT
```

**Nguyên nhân:** Node.js chưa được cài đặt hoặc chưa được thêm vào PATH.

**Cách khắc phục:**

```bash
# Kiểm tra Node.js đã cài đặt chưa
node --version

# Nếu chưa có, cài đặt từ:
# https://nodejs.org/en/download/
# Chọn phiên bản LTS (>= 18.x)

# Sau khi cài, khởi động lại VS Code / terminal
```

### Lỗi 6: Claude Code báo "MCP server disconnected"

**Triệu chứng:**

```
revit-mcp: disconnected
```

**Cách xử lý từng bước:**

```bash
# 1. Kiểm tra cú pháp JSON
node -e "console.log(JSON.parse(require('fs').readFileSync('.mcp.json','utf8')))"

# 2. Chạy MCP server thử bằng tay
node ./mcp-server/dist/index.js

# 3. Nếu lỗi, đọc thông báo lỗi và xử lý

# 4. Khởi động lại Claude Code
claude

# 5. Kiểm tra lại
/mcp
```

***

## Sơ đồ hoạt động toàn trình

Toàn bộ quá trình từ khi người dùng gõ prompt đến khi nhận kết quả:

```mermaid
flowchart TD
    Start([Người dùng nhập prompt]) --> A[AI nhận prompt]
    A --> B{Prompt cần<br/>gọi tool?}
    B -->|Không| C[AI trả lời<br/>trực tiếp]
    B -->|Có| D[AI chọn tool phù hợp]
    D --> E[AI tạo tool call<br/>với tham số]
    E --> F[MCP Server nhận<br/>tool call]
    F --> G[Chuyển thành<br/>HTTP request]
    G --> H{Revit Plugin<br/>phản hồi?}
    H -->|Không| I[Timeout Error]
    I --> J[AI thông báo lỗi<br/>cho người dùng]
    H -->|Có| K[Plugin gọi<br/>Revit API]
    K --> L{Revit API<br/>thành công?}
    L -->|Không| M[Trả về error message]
    M --> N[AI thông báo lỗi<br/>và gợi ý khắc phục]
    L -->|Có| O[Trả về dữ liệu JSON]
    O --> P[MCP Server<br/>chuyển kết quả]
    P --> Q{Cần gọi<br/>thêm tool?}
    Q -->|Có| D
    Q -->|Không| R[AI tổng hợp<br/>kết quả]
    R --> S([Hiển thị kết quả<br/>cho người dùng])
    C --> S

    style Start fill:#e3f2fd
    style S fill:#c8e6c9
    style I fill:#ffcdd2
    style N fill:#ffcdd2
```

***

## Câu hỏi tự suy nghĩ

### Câu 1: Thứ tự khởi động

Tại sao phải khởi động Revit trước, rồi đến MCP Server, rồi mới đến VS Code/Claude Code? Có thể đảo ngược thứ tự được không?

<details>

<summary>Xem trả lời</summary>

Lý do phải theo thứ tự này:

1. **Revit phải khởi động trước** vì plugin cần load và bắt đầu HTTP Listener. Nếu MCP Server chạy trước mà Revit chưa sẵn sàng, mọi HTTP request sẽ bị `Connection refused`.
2. **MCP Server khởi động sau Revit** để khi MCP Server kiểm tra kết nối đến Revit, nó sẽ nhận được phản hồi. Một số MCP Server có health check khi khởi động.
3. **IDE (VS Code/Claude Code) khởi động sau cùng** vì khi IDE kết nối đến MCP Server, nó sẽ yêu cầu danh sách tools. Nếu MCP Server chưa sẵn sàng, IDE sẽ đánh dấu MCP là `disconnected`.

Tuy nhiên, trong thực tế, nếu MCP Server có cơ chế **retry/reconnect**, bạn có thể khởi động theo thứ tự bất kỳ và chờ các thành phần tự kết nối. Nhưng để chắc chắn, hãy theo đúng thứ tự trên.

</details>

### Câu 2: Multi-tool và thứ tự thực thi

Khi AI cần gọi nhiều tool (ví dụ: get\_walls rồi create\_wall), AI có gọi tất cả cùng lúc không? Hay phải gọi tuần tự?

<details>

<summary>Xem trả lời</summary>

Phụ thuộc vào tính chất của các tool call:

* **Gọi tuần tự** khi kết quả của tool trước ảnh hưởng đến tool sau. Ví dụ: cần `get_walls` trước để biết có bao nhiêu tường, rồi mới quyết định tạo thêm bao nhiêu tường.
* **Gọi song song** khi các tool độc lập với nhau. Ví dụ: `get_walls` và `get_floors` có thể gọi đồng thời vì chúng không phụ thuộc kết quả của nhau.

AI (Claude/GPT) tự động quyết định chiến lược gọi dựa trên ngữ cảnh. Tuy nhiên, Revit API có giới hạn về thread-safe — nhiều lệnh ghi (create, delete) không nên chạy đồng thời vì chúng cần Transaction riêng.

</details>

### Câu 3: Bảo mật và quyền truy cập

Hệ thống hiện tại chạy trên localhost:8080 không có authentication. Điều gì xảy ra nếu ai đó trên cùng mạng gửi HTTP request đến port này?

<details>

<summary>Xem trả lời</summary>

Đây là một vấn đề bảo mật quan trọng:

1. **localhost** chỉ chấp nhận kết nối từ cùng máy tính, nên người trên mạng LAN không truy cập được (trừ khi plugin bind đến `0.0.0.0`).
2. Tuy nhiên, **bất kỳ ứng dụng nào trên cùng máy** đều có thể gửi request đến `localhost:8080`, ví dụ một trang web độc hại có thể dùng JavaScript để gọi API này (tấn công CSRF).
3. **Giải pháp:**
   * Thêm API key/token vào header của mỗi request
   * Giới hạn chỉ chấp nhận request từ MCP Server (kiểm tra User-Agent hoặc origin)
   * Sử dụng HTTPS với certificate tự ký
   * Bind chỉ đến `127.0.0.1` (không phải `0.0.0.0`)

Trong môi trường sản xuất, bắt buộc phải có authentication.

</details>

### Câu 4: Xử lý khi Revit bị treo

Nếu Revit bị treo (not responding) trong khi AI đang gọi tool, điều gì xảy ra?

<details>

<summary>Xem trả lời</summary>

Khi Revit bị treo:

1. **HTTP request sẽ timeout** — MCP Server sẽ chờ một khoảng thời gian (thường 30 giây) rồi trả về lỗi timeout.
2. **MCP Server báo lỗi cho AI** — AI sẽ nhận được error response thay vì dữ liệu.
3. **AI thông báo cho người dùng** — AI sẽ nói "Không thể kết nối đến Revit" hoặc tương tự.
4. **Các tool call tiếp theo cũng sẽ lỗi** cho đến khi Revit hoạt động trở lại.

**Cách xử lý:**

* Đặt timeout hợp lý trong MCP Server (30-60 giây)
* Thêm retry logic với exponential backoff
* Hiển thị thông báo rõ ràng cho người dùng để họ biết cần khởi động lại Revit

</details>

### Câu 5: Mở rộng hệ thống

Nếu muốn thêm một tool mới (ví dụ: `get_rooms` để lấy danh sách phòng), cần sửa những file nào?

<details>

<summary>Xem trả lời</summary>

Cần sửa 3 file theo 3 tầng (layer):

**1. Revit Plugin (C#)** — Thêm logic truy vấn Revit API:

```csharp
// File: Tools/GetRoomsTool.cs
public class GetRoomsTool
{
    public static string Execute(Document doc)
    {
        var rooms = new FilteredElementCollector(doc)
            .OfCategory(BuiltInCategory.OST_Rooms)
            .Cast<Room>()
            .Select(r => new { Id = r.Id.IntegerValue, Name = r.Name })
            .ToList();
        return JsonConvert.SerializeObject(rooms);
    }
}
```

**2. Revit Plugin (C#)** — Thêm route trong HTTP Listener:

```csharp
// File: RevitMcpListener.cs -- thêm case mới
"/api/get-rooms" => GetRoomsTool.Execute(_doc),
```

**3. MCP Server (Node.js/TypeScript)** — Đăng ký tool mới:

```typescript
server.tool(
  "get_rooms",
  "Lấy danh sách tất cả phòng trong Revit model",
  {},
  async () => {
    const response = await fetch("http://localhost:8080/api/get-rooms");
    const rooms = await response.json();
    return { content: [{ type: "text", text: JSON.stringify(rooms, null, 2) }] };
  }
);
```

Không cần thay đổi gì phía AI (VS Code/Claude Code) — AI tự động nhận diện tool mới khi MCP Server đăng ký.

</details>

***

## Tổng kết

### Những gì đã học trong bài này

| Nội dung             | Chi tiết                                |
| -------------------- | --------------------------------------- |
| **Thứ tự khởi động** | Revit → Plugin → MCP Server → IDE       |
| **Kiểm tra kết nối** | Lệnh `/mcp`, curl health check          |
| **Test đơn giản**    | get\_walls — đọc dữ liệu từ Revit       |
| **Test ghi dữ liệu** | create\_wall — tạo element mới          |
| **Test phức tạp**    | Multi-tool — kết hợp nhiều lệnh         |
| **Debug**            | Xem logs, kiểm tra từng lớp kết nối     |
| **Xử lý lỗi**        | 6 loại lỗi thường gặp và cách khắc phục |

### Checklist hoàn thành Bài 7

* [ ] Khởi động thành công toàn bộ hệ thống theo đúng thứ tự
* [ ] Lệnh `/mcp` hiển thị `connected` và đầy đủ tools
* [ ] Test `get_walls` trả về dữ liệu đúng
* [ ] Test `create_wall` tạo được tường mới trong Revit
* [ ] Test multi-tool thực hiện được nhiều bước liên tiếp
* [ ] Biết cách đọc logs và debug khi có lỗi

### Bài tiếp theo

Trong **Bài 8: Mở rộng — Viết thêm Tool**, chúng ta sẽ:

* Thêm nhiều tool mới: `get_floors`, `create_column`, `get_rooms`, ...
* Học cách viết prompt phức tạp để AI sử dụng nhiều tool thông minh
* Tìm hiểu prompt engineering cho BIM

***

## Tài liệu tham khảo

| Tài liệu                                              | Link                                                                                                           |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| **DeepBIM Revit MCP Plugin** (Source code tham khảo)  | [github.com/nguyenngocdue/deepbim-revit-mcp-plugin](https://github.com/nguyenngocdue/deepbim-revit-mcp-plugin) |
| **Revit MCP Server** (tham chiếu tool/server thực tế) | [github.com/nguyenngocdue/revit-mcp-server](https://github.com/nguyenngocdue/revit-mcp-server)                 |
| **MCP Specification**                                 | [spec.modelcontextprotocol.io](https://spec.modelcontextprotocol.io/)                                          |
| **MCP TypeScript SDK**                                | [github.com/modelcontextprotocol/typescript-sdk](https://github.com/modelcontextprotocol/typescript-sdk)       |
| **Revit API Documentation**                           | [revitapidocs.com](https://www.revitapidocs.com/)                                                              |
| **Claude Code Documentation**                         | [docs.anthropic.com/claude-code](https://docs.anthropic.com/en/docs/claude-code)                               |
| **VS Code MCP Support**                               | [code.visualstudio.com/docs](https://code.visualstudio.com/docs)                                               |

***

> *"Khi tất cả các thành phần hoạt động cùng nhau, bạn không còn viết code để điều khiển Revit — bạn chỉ cần nói cho AI biết bạn muốn gì."*

***

[<< Bài 6: Chuẩn bị sẵn sàng kết nối](/revit-mcp-ai/phan-3-ket-noi-and-van-hanh/bai-6.md) | [Bài 8: Mở rộng — Viết thêm Tool >>](/revit-mcp-ai/phan-4-nang-cao-and-hoan-thien/bai-8.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://deepbim.gitbook.io/revit-mcp-ai/phan-3-ket-noi-and-van-hanh/bai-7.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
