Skip to main content
Glama
tungvt93

local-code-intel-mcp

by tungvt93
README.md
# Local Code Intelligence MCP

Monorepo: indexer (SQLite + embedding local tùy chọn), MCP server (8 tools), CLI `codeintel`.

## Làm từ đầu — từng bước và lệnh

Dưới đây là thứ tự làm việc đầy đủ. Thay `~/Documents/Code/grapuco-mcp` bằng đường dẫn thật tới monorepo trên máy bạn; thay `~/Documents/Code/my-app` bằng thư mục dự án muốn index (nơi sẽ xuất hiện `.codeintel/`).

| Bước | Việc cần làm | Lệnh (ví dụ) |
|------|----------------|----------------|
| 0 | (Nếu chưa có mã) clone repo và vào thư mục monorepo | `git clone <url-repo> grapuco-mcp` rồi `cd grapuco-mcp` |
| 1 | Kiểm tra Node ≥ 20 | `node -v` |
| 2 | Bật pnpm 9 qua Corepack (một lần trên máy) | `corepack enable` → `corepack prepare pnpm@9.15.4 --activate` |
| 3 | Cài dependency và build toàn bộ | `cd ~/Documents/Code/grapuco-mcp` → `pnpm install` → `pnpm build` |
| 4 | Chuyển vào **thư mục dự án** (workspace được index) | `cd ~/Documents/Code/my-app`<br>*(hoặc thử:* `cd ~/Documents/Code/grapuco-mcp/examples/nest-sample`*)* |
| 5 | Khởi tạo CodeIntel (tạo `.codeintel/`, `config.json`) | `node ~/Documents/Code/grapuco-mcp/apps/cli/dist/index.js init` |
| 6 | *(Khuyến nghị)* Sửa `.codeintel/config.json` — `include` / `exclude` | Mở file bằng editor |
| 7 | Index toàn bộ mã nguồn | `node ~/Documents/Code/grapuco-mcp/apps/cli/dist/index.js index --full` |
| 8 | Kiểm tra DB và cấu hình | `node ~/Documents/Code/grapuco-mcp/apps/cli/dist/index.js stats`<br>`node ~/Documents/Code/grapuco-mcp/apps/cli/dist/index.js doctor` |
| 9 | *(Tùy chọn)* Tự reindex khi file đổi | `node ~/Documents/Code/grapuco-mcp/apps/cli/dist/index.js watch` *(Ctrl+C để dừng)* |
| 10 | *(Tùy chọn)* Gắn MCP vào Cursor: thêm server với `cwd` = thư mục bước 4 | Xem mục **Kết nối MCP** bên dưới |
| 11 | Lưu cấu hình MCP và khởi động lại Cursor (hoặc reload MCP) | Theo giao diện Cursor |

**Một khối lệnh copy được** (sau khi chỉnh đường dẫn cho đúng):

```bash
# --- A. Build monorepo (một lần hoặc sau khi pull code mới) ---
cd ~/Documents/Code/grapuco-mcp
corepack enable
corepack prepare pnpm@9.15.4 --activate
pnpm install
pnpm build

# --- B. Index một dự án ---
export CODEINTEL_REPO=~/Documents/Code/grapuco-mcp
export MY_APP=~/Documents/Code/my-app   # hoặc: $CODEINTEL_REPO/examples/nest-sample

cd "$MY_APP"
node "$CODEINTEL_REPO/apps/cli/dist/index.js" init
node "$CODEINTEL_REPO/apps/cli/dist/index.js" index --full
node "$CODEINTEL_REPO/apps/cli/dist/index.js" stats
```

## Yêu cầu hệ thống

- **Node.js** >= 20  
- **pnpm** 9 (repo khai báo `packageManager: pnpm@9.15.4`)

Bật pnpm qua Corepack (khuyến nghị):

```bash
corepack enable
corepack prepare pnpm@9.15.4 --activate
```

## Cài đặt từ mã nguồn

Trong thư mục gốc monorepo:

```bash
pnpm install
pnpm build
```

(`pnpm build` chạy Turbo và build toàn bộ `apps/*`, `packages/*`.)

Sau khi build, binary CLI nằm tại `apps/cli/dist/index.js` (lệnh `codeintel`), MCP nằm tại `apps/mcp-server/dist/index.js` (lệnh `codeintel-mcp` nếu cài link).

## Sử dụng CLI

CLI cần chạy trong **thư mục dự án** mà bạn muốn index (nơi sẽ có `.codeintel/`).

### Cách gọi lệnh

**1. Qua `node` (ổn định, không cần link):** từ monorepo, dùng đường dẫn tương đối tới bản build:

```bash
node /đường/dẫn/tới/grapuco-mcp/apps/cli/dist/index.js init
node /đường/dẫn/tới/grapuco-mcp/apps/cli/dist/index.js index --full
node /đường/dẫn/tới/grapuco-mcp/apps/cli/dist/index.js stats
```

**2. Qua pnpm workspace** (khi đang đứng ở root monorepo):

```bash
pnpm --filter @codeintel/cli exec codeintel -- init
pnpm --filter @codeintel/cli exec codeintel -- index --full
pnpm --filter @codeintel/cli exec codeintel -- stats
```

Lưu ý: các lệnh trên dùng `process.cwd()` của shell — hãy `cd` vào đúng thư mục dự án trước khi chạy.

**3. Link global (tùy chọn):**

```bash
cd apps/cli && pnpm link --global
```

Sau đó trong thư mục dự án: `codeintel init`, `codeintel index --full`, v.v.

### Luồng làm việc thông thường

1. **`init`** — tạo cấu trúc `.codeintel/` và `config.json` mặc định (database, vectors, logs, cache).  
2. **`index --full`** — index toàn bộ workspace theo `include` / `exclude` trong config.  
3. **`doctor`** — kiểm tra cơ bản rằng config và schema DB đang hiện diện.  
4. **`stats`** — xem số dòng các bảng chính và breakdown cache hiện có.  
5. **`minimal-context --mode deep`** — lấy thứ tự đọc file ngắn gọn cho task hiện tại trước khi mở code.  
6. **`trace-flow --kind route --mode deep`** — lần theo execution flow cho route; đây là đường dùng chín nhất hiện nay.  
7. **`watch`** (tùy chọn) — tự reindex khi file thay đổi.  
8. **`mcp:start`** — khởi chạy MCP server trỏ vào workspace hiện tại (xem phần dưới).

### Các lệnh CLI

| Lệnh | Mô tả |
|------|--------|
| `codeintel init` | Tạo `.codeintel/` và `config.json` |
| `codeintel index [--full]` | Index gia tăng hoặc full (`--full`) |
| `codeintel reindex [file...]` | Reindex danh sách file (hoặc rỗng) |
| `codeintel watch` | Theo dõi file và reindex theo batch |
| `codeintel trace-flow [value] [--kind route\|symbol\|task] [--method GET] [--mode lite\|deep] [--max-depth N]` | In execution flow tổng quát; `route` là path chín nhất, còn `symbol` / `task` hiện là fallback sớm |
| `codeintel minimal-context [task] [--mode lite\|deep] [--max-files N]` | Gợi ý thứ tự đọc file tối thiểu cho một task cụ thể |
| `codeintel mcp:start` (hoặc `mcp`) | Spawn MCP server, `CODEINTEL_CWD` = thư mục hiện tại |
| `codeintel doctor` | Kiểm tra cơ bản config + DB/schema presence |
| `codeintel stats` | In số dòng các bảng chính trong DB, kèm breakdown `flow_cache.execution_flow` và `flow_cache.minimal_context` |
| `codeintel config:show` | In nguyên `config.json` |

### Workflow gợi ý cho bugfix / refactor

1. Chạy `codeintel doctor` để xác nhận config và schema cơ bản đã sẵn sàng.
2. Chạy `codeintel stats` để xem DB có `routes`, `calls`, `module_roles`, `side_effect_edges`, và cache hiện tại đang có gì.
3. Chạy `codeintel minimal-context "mô tả task" --mode deep` để lấy danh sách `Read first` / `Then check` trước khi mở editor.
4. Nếu task bám theo route rõ ràng, chạy `codeintel trace-flow "/api/orders/:id" --kind route --method GET --mode deep` để thấy flow tổng quát.
5. Khi cần chi tiết hơn ở cấp symbol, dùng MCP tools như `search_symbols`, `get_symbol_context`, `find_callers`, `find_callees`.

### Ghi chú về cache

- Deep `trace-flow` cho `route` và deep `minimal-context` dùng bảng `flow_cache` để tái sử dụng payload đã tính.
- `codeintel stats` sẽ in tổng số record trong `flow_cache` và breakdown theo `execution_flow` / `minimal_context` để dễ kiểm tra thủ công.
- Purge incremental hiện xóa `flow_cache` toàn cục để tránh cache cũ sau reindex.
- Cache key có kèm version salt nội bộ để tự nhiên bust cache khi query contract/algorithm thay đổi.

### Cấu hình

File `.codeintel/config.json` (sau `init`) điều khiển:

- `workspaceRoot`, `dbPath`, `vectorDir`
- `enabledLanguages`, `include`, `exclude`
- `watch`, `embeddings` (bật/tắt embedding local, v.v.)

Chỉnh `include` / `exclude` cho khớp cấu trúc repo của bạn, rồi chạy lại `index --full` khi cần.

### Ví dụ với `examples/nest-sample`

```bash
cd examples/nest-sample
node ../../apps/cli/dist/index.js init
node ../../apps/cli/dist/index.js index --full
node ../../apps/cli/dist/index.js stats
```

(Đường dẫn `../../apps/cli/...` giả định bạn đang ở trong cây monorepo `grapuco-mcp`.)

## Kết nối MCP (Cursor / client khác)

MCP server đọc workspace qua biến **`CODEINTEL_CWD`** (thư mục đã `init` và đã index). Nếu không set, server dùng `process.cwd()`.

**Cách A — Dùng CLI làm launcher** (đặt `cwd` = thư mục dự án đã index):

```json
{
  "mcpServers": {
    "codeintel": {
      "command": "node",
      "args": ["/ABS/PATH/grapuco-mcp/apps/cli/dist/index.js", "mcp:start"],
      "cwd": "/ABS/PATH/dự-an-của-bạn"
    }
  }
}
```

**Cách B — Gọi thẳng MCP server:**

```json
{
  "mcpServers": {
    "codeintel": {
      "command": "node",
      "args": ["/ABS/PATH/grapuco-mcp/apps/mcp-server/dist/index.js"],
      "env": {
        "CODEINTEL_CWD": "/ABS/PATH/dự-an-của-bạn"
      }
    }
  }
}
```

Thay `/ABS/PATH/...` bằng đường dẫn thật trên máy. Trước khi dùng MCP, hãy đảm bảo đã chạy `codeintel init` và `codeintel index --full` trong `dự-an-của-bạn`.

### Nhiều dự án: `cwd` / `CODEINTEL_CWD` thế nào?

Mỗi lần chạy MCP là **một tiến trình gắn với đúng một thư mục dự án** (nơi đã có `.codeintel/config.json` và DB). Bạn **không** thể dùng một dòng `cwd` duy nhất trỏ “cả hai repo” — mỗi repo đã `init`/`index` riêng thì cần **một cặp** (tên server + `cwd` hoặc `CODEINTEL_CWD`) riêng.

**Cách làm:** khai báo **nhiều MCP server** trong Cursor, **tên key** (`codeintel-api`, `codeintel-web`, …) khác nhau, còn `command`/`args` có thể giống nhau:

```json
{
  "mcpServers": {
    "codeintel-biomics": {
      "command": "node",
      "args": ["/ABS/PATH/grapuco-mcp/apps/cli/dist/index.js", "mcp:start"],
      "cwd": "/ABS/PATH/biomics-app"
    },
    "codeintel-khach": {
      "command": "node",
      "args": ["/ABS/PATH/grapuco-mcp/apps/cli/dist/index.js", "mcp:start"],
      "cwd": "/ABS/PATH/du-an-khac"
    }
  }
}
```

Tương đương nếu gọi thẳng `mcp-server`: mỗi entry có `env.CODEINTEL_CWD` trỏ về **root** từng dự án (đã `init` + `index`).

**Trong chat:** nói rõ agent nên gọi tool trên server nào (ví dụ “dùng MCP **codeintel-biomics**”) để đúng DB.

**Một dự án tại một thời điểm:** chỉ cần một server; khi đổi dự án có thể sửa `cwd` / `CODEINTEL_CWD` và reload MCP (không cần nhiều entry).

## Gói chính trong monorepo

| Package | Vai trò |
|---------|---------|
| `apps/mcp-server` | MCP stdio, tools `search_symbols`, `get_symbol_context`, … |
| `apps/indexer` | Pipeline index full / incremental |
| `apps/cli` | `init`, `index`, `reindex`, `watch`, `mcp:start`, `doctor`, `stats`, `config:show` |
| `packages/graph` | Truy vấn SQLite (symbols, calls, routes, impact) |
| `packages/embeddings` | Embedding local + cosine search |
| `packages/storage` | SQLite + schema |

## Hướng dẫn dùng MCP tools

Mỗi tool trả về **một block văn bản** chứa **JSON** (đã pretty-print). Client (ví dụ Cursor) sẽ nhận chuỗi đó; bạn có thể parse JSON để đọc `results`, `callers`, v.v.

**Điều kiện:** MCP phải trỏ đúng dự án đã `init` + `index --full` (xem **Kết nối MCP**). Dữ liệu chỉ phản ánh những file đã được indexer xử lý theo `include` / `enabledLanguages` trong `.codeintel/config.json`.

### Luồng gợi ý cho agent

1. Gọi **`search_symbols`** với từ khóa (tên hàm, class, một phần `fqName`) để lấy danh sách ứng viên và `symbolId`.
2. Dùng **`symbolId`** hoặc chuỗi **`symbol`** (khớp `name` hoặc `fq_name` đầu tiên trong DB) cho các tool còn lại.
3. Khi task còn rộng, gọi **`get_minimal_context_for_task`** trước để lấy read order tối thiểu thay vì mở quá nhiều file.
4. Muốn hiểu nhanh một symbol: **`get_symbol_context`** (gồm callers/callees lân cận).
5. Muốn xem execution flow tổng quát: ưu tiên **`trace_execution_flow`** với `route`; `symbol` / `task` hiện chỉ là đường fallback sớm và có thể trả về placeholder/low-confidence.
6. Muốn đi sâu call graph: **`find_callers`** / **`find_callees`** hoặc **`analyze_impact`**.
7. Dự án Nest có route đã index và cần output legacy theo controller → handler → callees: **`trace_route_flow`**.
8. Tìm theo ngữ nghĩa (embedding): **`search_code_semantic`** — cần `embeddings.enabled: true` trong config và đã index sau khi bật.
9. Tổng quan file/module: **`summarize_module`**.

### `get_minimal_context_for_task`

Trả về một bundle đọc nhanh cho task đang làm, chia thành `readFirst`, `thenCheck`, `skipForNow`, và `unknownsToVerify`.

| Tham số | Bắt buộc | Mô tả |
|--------|----------|--------|
| `task` | Có | Mô tả task bằng ngôn ngữ tự nhiên, ví dụ `fix login bug in auth flow`. |
| `mode` | Không | `lite` hoặc `deep` (khuyến nghị `deep` khi cần đọc ít file nhưng đúng hơn). |
| `maxFiles` | Không | Số file tối đa muốn ưu tiên trong bundle. |

**Kết quả:** object có `readFirst[]`, `thenCheck[]`, `skipForNow[]`, `unknownsToVerify[]`; deep mode có thể trả thêm `cacheHit` để cho biết bundle được lấy từ `flow_cache`.

### `trace_execution_flow`

Theo dõi execution flow tổng quát cho ba loại entrypoint: `route`, `symbol`, `task`. Hiện tại `route` là path chín nhất; `symbol` và `task` vẫn là entrypoint sớm/fallback nên có thể chỉ trả về kết quả giới hạn hoặc low-confidence placeholder.

Với Laravel/Rails trong wave hiện tại, route/controller flow đã usable cho các convention phổ biến; ORM/model awareness cũng đã usable cho các pattern Eloquent/ActiveRecord thông dụng. Các route DSL phức tạp hơn, app namespace-heavy, hoặc nhánh macro-heavy / dynamic metaprogramming vẫn có thể xuất hiện `unknowns` hoặc `alternatives` thay vì một flow hoàn toàn deterministic.

| Tham số | Bắt buộc | Mô tả |
|--------|----------|--------|
| `entryKind` | Có | `route`, `symbol`, hoặc `task`. Khuyến nghị dùng `route` khi có thể. |
| `value` | Có | Route path, symbol name/fqName, hoặc task text. |
| `method` | Không | Chỉ dùng khi `entryKind = route`; mặc định `GET`. |
| `mode` | Không | `lite` hoặc `deep`. |
| `maxDepth` | Không | Giới hạn số hop call graph cần đi. |

**Kết quả:** object gồm `summary`, `segments`, `alternatives`, `confidence`, `unknowns`; deep route mode có thể trả thêm `cacheHit` khi payload được đọc lại từ `flow_cache`. Với `symbol` / `task`, hiện nên xem output như tín hiệu thử nghiệm hơn là flow đầy đủ.

### `search_symbols`

Tìm symbol theo text (SQL `LIKE` trên `name` và `fq_name`), xếp hạng nội bộ rồi trả về tối đa `k` kết quả.

| Tham số | Bắt buộc | Mô tả |
|--------|----------|--------|
| `query` | Có | Chuỗi tìm kiếm (ví dụ `UserService`, `getOrder`). |
| `k` | Không | Số kết quả tối đa (mặc định `10`). |
| `kind` | Không | Lọc thêm theo loại symbol (ví dụ `class`, `function`) sau khi truy vấn. |

**Kết quả:** `results[]` — mỗi phần tử có `symbolId`, `name`, `fqName`, `kind`, `filePath`, `startLine`, `endLine`, `score`.

### `get_symbol_context`

Một “ảnh chụp” symbol: thông tin symbol + callers/callees trong bán kính `radius` (số bước trên đồ thị gọi, mặc định `1`).

| Tham số | Bắt buộc | Mô tả |
|--------|----------|--------|
| `symbolId` | Một trong hai | UUID symbol trong DB (nên lấy từ `search_symbols`). |
| `symbol` | Một trong hai | Tên hoặc `fq_name` (symbol đầu tiên khớp). |
| `radius` | Không | Độ sâu lân cận (mặc định `1`). |

**Kết quả:** `symbol`, `callers`, `callees` — hoặc `{ "error": "symbol not found" }`.

### `find_callers` / `find_callees`

| Tham số | Bắt buộc | Mô tả |
|--------|----------|--------|
| `symbolId` / `symbol` | Một trong hai | Giống `get_symbol_context`. |
| `k` | Không | Giới hạn số cạnh trả về (mặc định `20`). |

**Kết quả:** `find_callers` → `target` (fqName) + `callers`. `find_callees` → `source` + `callees`. Nếu không tìm thấy symbol: `error` + mảng rỗng.

### `trace_route_flow`

Theo dõi luồng **Nest-style** đã được indexer gắn với bảng `routes` (HTTP `method` + `path`).

| Tham số | Bắt buộc | Mô tả |
|--------|----------|--------|
| `method` | Có | Ví dụ `GET`, `POST` (theo cách lưu trong DB). |
| `path` | Có | Đường dẫn route, ví dụ `/users/:id`. |

**Kết quả:** object mô tả luồng controller → service → … — hoặc `{ "error": "route not found", "route": { ... } }` nếu không có route khớp.

### `analyze_impact`

BFS **callers** từ một symbol để ước lượng tác động khi sửa (ai gọi tới ai).

| Tham số | Bắt buộc | Mô tả |
|--------|----------|--------|
| `symbolId` / `symbol` | Một trong hai | Giống trên. |
| `depth` | Không | Độ sâu BFS (mặc định `4`). |

**Kết quả:** `origin` (fqName) + `impacted` (danh sách các symbol bị ảnh hưởng theo chỉ mục).

### `search_code_semantic`

Embedding **local** (cosine similarity) trên các bản ghi trong bảng `embeddings`. Cần đã index khi `embeddings.enabled` bật (mặc định trong config).

| Tham số | Bắt buộc | Mô tả |
|--------|----------|--------|
| `query` | Có | Mô tả bằng ngôn ngữ tự nhiên hoặc đoạn code tương tự. |
| `k` | Không | Số kết quả (mặc định `10`). |
| `pathPrefix` | Không | Giới hạn symbol thuộc file có `path` khớp (ký tự `*` được thay bằng `%` trong SQL `LIKE`). |

**Kết quả:** `results[]` gồm `symbolId`, `name`, `fqName`, `kind`, `filePath`, dòng, `score`. Nếu chưa có embedding trong DB, danh sách có thể rỗng.

### `summarize_module`

Tóm tắt nhanh theo **đường dẫn file** hoặc chuỗi `module` (dùng cho mệnh đề `LIKE` trên `f.path`).

| Tham số | Bắt buộc | Mô tả |
|--------|----------|--------|
| `filePath` | Một trong hai | Ví dụ `src/auth/login.ts` hoặc đoạn đặc trưng trong path. |
| `module` | Một trong hai | Tương tự, thay thế khi không truyền `filePath`. |

**Kết quả:** `summary` (dòng mô tả số symbol / route), `topSymbols` (tối đa 12), `routes` (các route Nest gắn với file khớp). Phải truyền ít nhất một tham số; nếu không: `{ "error": "filePath or module required" }`.

### Prompt ví dụ (Cursor / chat)

Dán vào chat và **thay** tên class/hàm/path/route/module bằng thứ có trong dự án đã index. Nhắc agent dùng đúng **MCP CodeIntel** (tên server bạn đặt trong `mcp.json`, ví dụ `codeintel`).

**Luồng nhiều bước (khuyến nghị khi refactor):**

```text
Dùng MCP local code intelligence: gọi search_symbols với query "PaymentService", 
chọn đúng symbol khớp dự án, sau đó get_symbol_context với symbolId đó (radius 2), 
rồi find_callers và analyze_impact (depth 4). Tóm tắt ai gọi tới service này và 
những chỗ có thể bị ảnh hưởng khi đổi chữ ký hàm.
```

**`search_symbols`:**

```text
Qua MCP CodeIntel, chạy search_symbols: query "validateToken", k 15. 
Liệt kê top kết quả kèm filePath và dòng.
```

```text
Tìm mọi symbol class liên quan tới "User" bằng search_symbols (query "User", kind "class").
```

**`get_symbol_context`:**

```text
Dùng MCP: get_symbol_context cho symbol "OrderController#create" (hoặc symbolId nếu đã có), radius 2. 
Giải thích mối quan hệ callers/callees.
```

**`find_callers` / `find_callees`:**

```text
MCP find_callers: symbol "sendNotificationEmail", k 25 — ai đang gọi hàm này?
```

```text
MCP find_callees: symbol "CheckoutService.process" — hàm đó gọi những symbol nào?
```

**`trace_route_flow` (Nest):**

```text
MCP trace_route_flow: method GET, path /api/orders/:orderId — luồng xử lý từ controller xuống service.
```

**`analyze_impact`:**

```text
Nếu sửa hàm "calculateTax" trong codebase đã index, dùng MCP analyze_impact (symbol đó, depth 5). 
Liệt kê các symbol / file cần review.
```

**`search_code_semantic`:**

```text
MCP search_code_semantic: query "xử lý refund và hoàn tiền cho đơn hủy", k 8. 
Ưu tiên symbol có score cao và chỉ rõ file.
```

```text
Tìm code liên quan tới "JWT refresh token rotation" bằng search_code_semantic, 
pathPrefix "src/auth" (hoặc thư mục tương đương trong repo).
```

**`summarize_module`:**

```text
MCP summarize_module: filePath "src/features/billing" — tóm tắt symbol chính và route (nếu có).
```

**Gợi ý:** Trong Cursor, có thể thêm câu *“Chỉ dùng dữ liệu từ MCP, không đoán path nếu JSON không có.”* để giảm hallucination.

## License

Private / theo dự án của bạn.