
這篇記錄 [`content-i18n`](https://github.com/loustack17/content-i18n) 同時支援 CLI 和 MCP server 時的 system design

重點不是 MCP protocol 本身, 而是同一個 workflow 要怎麼被兩種 entrypoint 共用

核心問題:

- CLI 是什麼
- MCP 是什麼
- 它們各自應該負責什麼
- 為什麼不能各自長一套 workflow
- `content-i18n` 最後怎麼切 core, CLI, MCP

## 什麼是 CLI

CLI 是 command-line interface, 也就是人透過 terminal 下 command 操作程式

例如:

```bash
content-i18n review --config ./content-i18n.yaml --file target.md --source source.md
```

這個 command 裡有幾個部分:

- `content-i18n`: 要執行的程式
- `review`: 要執行的 action
- `--config`: config file path
- `--file`: target file
- `--source`: source file

CLI 的使用者通常是人, shell script, CI job, 或其他 automation

一個好的 CLI 通常要處理:

- command name 清楚
- flag 好理解
- error message 看得懂
- exit code 可以給 script 判斷
- output 可以給人讀, 也可以給 shell 接

CLI 是 human-facing entrypoint

## CLI 怎麼運作

CLI 大概是這條 flow:

```text
command
  -> argument parsing
  -> config loading
  -> core workflow call
  -> human-facing output
```

以 `content-i18n review` 來說:

1. 讀 command 和 flags
2. 載入 `content-i18n.yaml`
3. 找到 source 和 target file
4. 呼叫真正的 review workflow
5. 把結果印給使用者
6. 根據結果回傳 exit code

CLI 可以決定 output 怎麼印, 錯誤訊息怎麼呈現, command 怎麼命名

但 review 檢查什麼, queue state 怎麼算, sync-status 什麼時候能更新, 不應該放在 CLI layer

## CLI layer 要怎麼設計

CLI layer 適合負責:

- command tree
- flag parsing
- config path loading
- shell-friendly output
- human-readable error
- exit code

CLI layer 不適合負責:

- prepare 的 business rule
- review 的 business rule
- queue state 推導
- sync-status 判斷
- batch orchestration
- source / target mapping rule

CLI 是 adapter:

```text
human
  -> CLI
  -> internal/core
```

它把人的 command 轉成 core workflow 可以吃的 input, 再把 core workflow 的 result 轉成人看得懂的 output

## 什麼是 MCP

MCP 是 Model Context Protocol

在這個專案裡, MCP 讓 agent runtime 可以用 structured tool call 操作本機工具

如果 CLI 是 human 跟 local program 之間的 contract, MCP 就是 agent runtime 跟 local program 之間的 contract

CLI call:

```bash
content-i18n review --config ./content-i18n.yaml --file target.md --source source.md
```

MCP tool call:

```json
{
  "tool": "content_i18n_review_translation",
  "file": "target.md",
  "source": "source.md"
}
```

CLI 是 command + flags

MCP 是 tool name + structured arguments

形式不同, 但最後要做的事情可以是同一件事

## MCP 怎麼運作

MCP layer 大概是這條 flow:

```text
tool definition
  -> request decoding
  -> core workflow call
  -> structured response
```

一支 MCP tool 通常要定義:

- tool name
- description
- input schema
- handler
- response shape

以 `content_i18n_review_translation` 來說:

1. 宣告這支 tool 存在
2. 定義它需要哪些 arguments
3. 接 agent runtime 傳進來的 request
4. decode 成 core workflow 可以用的 input
5. 呼叫 review workflow
6. 回傳 structured response 給 agent

MCP 的 output 要讓 agent 可以接著做下一步

所以 response 通常要包含:

- issue list
- file path
- source path
- sync readiness
- next action hint

但 MCP 不該自己實作 review

## MCP layer 要怎麼設計

MCP layer 適合負責:

- public tool contract
- argument schema
- request decoding
- handler registration
- structured response
- agent-friendly result shape

MCP layer 不適合負責:

- 自己決定 review rule
- 自己推導 queue state
- 自己判斷 completion
- 自己處理 source / target mapping
- 自己補 batch orchestration rule

`content-i18n` 的 MCP server 後來整理成:

```text
internal/mcp/
  server.go
  tools.go
  tool_defs.go
  tool_handlers.go
  resources.go
  resource_defs.go
  resource_handlers.go
  response.go
```

這個結構讓責任比較清楚:

- definition 放 tool contract
- handler 接 request 並呼叫 core
- registration 負責把 tool 掛到 server
- response 負責 agent-facing output shape

registration 也改成 registry / spec pattern

這樣 public contract 不會藏在一大段手工註冊裡, 新增或移除 tool 時也比較不容易漏掉

## 為什麼 CLI 和 MCP 要共用同一個 core workflow

同一個工具支援 CLI 和 MCP 時, 最大風險是兩邊慢慢變成兩個產品

如果兩邊各自實作邏輯, 很快就會 drift:

- CLI review pass, MCP review fail
- MCP sync 成功, CLI queue 還是 stale
- batch path 和 single-file path 檢查不一致
- agent 繞過 review, 直接改 target file
- completion rule 在不同入口有不同解釋

所以 workflow 必須只有一個 authority

在 `content-i18n` 裡, 這個 authority 是 `internal/core`

```text
CLI / MCP = interface
internal/core = workflow owner
```

CLI 和 MCP 可以有不同 input / output

但 prepare, review, sync, queue, batch 的 meaning 只能有一份

## `content-i18n` 的 core boundary

最後比較穩定的分層:

```text
consumer repo
  ├─ content roots
  ├─ content-i18n.yaml
  ├─ glossary
  └─ style pack
        │
        ▼
CLI / MCP
        │
        ▼
internal/core
  ├─ init
  ├─ prepare/review/sync
  ├─ queue
  ├─ batch orchestration
  └─ site validation entrypoint
        │
        ├─ validator
        ├─ structure
        ├─ frontmatter
        ├─ content
        └─ providers
```

責任切分:

| Layer | Responsibility |
|---|---|
| CLI | command parsing, flags, config path, exit code, human-readable output |
| MCP | tool definition, request decoding, handler registration, structured response |
| internal/core | prepare, review, sync, queue, batch, validation workflow |
| validator / structure / frontmatter / content | low-level document rules |
| providers | DeepL, Google, ai-harness provider boundary |

CLI 和 MCP 呼叫 `internal/core`

`internal/core` 不需要知道 caller 是 CLI 還是 MCP

Review rule 改一次, CLI 和 MCP 都會吃到同一個行為

## MCP tool surface 要怎麼設計

MCP tool surface 不能太碎

早期如果把 tool 拆成 low-level primitive, 看起來很有彈性:

- read source
- create work packet
- validate translation
- write translation target
- next translation
- repair translation

但這對 agent 反而危險

因為 tool surface 不只是 API 清單, 它也在暗示 agent 應該怎麼做事

raw primitive 太多, agent 很容易自己組 workflow:

```text
read source
  -> inspect file
  -> edit target directly
  -> run partial validation
  -> skip sync-status
```

這就是 tool bypass

所以 `content-i18n` 後來把 public MCP surface 收斂成 workflow-level tools:

- `content_i18n_status`
- `content_i18n_prepare_translation`
- `content_i18n_review_translation`
- `content_i18n_sync_status`
- `content_i18n_translation_queue`
- `content_i18n_translate_batch`
- `content_i18n_validate_site`

這 7 支 tool 比 low-level primitive 更適合 agent:

- `prepare_translation` 比 `read_source` + `create_work_packet` 更接近真實 workflow step
- `translation_queue` 已經能帶出 candidate, 不需要額外 `next_translation`
- `review_translation` 回 structured issue 和 sync readiness, 比單純 validator wrapper 更完整
- `translate_batch` 保留 batch orchestration, 不讓 agent 自己拼 batch loop
- `sync_status` 把 completion 變成正式狀態, 不只是檔案存在

比較好的 MCP tool design:

- public tool 少
- operation 高階
- response 完整
- caller 不容易自己補 workflow

## Queue state 怎麼運作

Queue model 讓 translation workflow 從一次性任務變成可維護系統

`content-i18n` 用三種 state:

- `completed`
- `stale`
- `missing`

這些 state 從下面資料推導:

- source discovery
- expected target path
- source hash
- translation status

三種 state 的意思:

| State | Meaning |
|---|---|
| `missing` | source exists, expected target does not exist |
| `stale` | target exists, but source changed after last sync |
| `completed` | review / validation passed, target synced with source |

翻譯不是 once-and-done

今天完成的 target, 只要 source 改了, 明天就可能 stale

所以 queue 不只是列出待翻譯檔案, 它是在回答:

- 哪些 target 不存在
- 哪些 target 已經跟 source 不同步
- 哪些 target 真的完成
- 下一個該處理哪一篇

MCP tool 也要尊重 queue model

如果 agent 可以跳過 queue, review, sync, workflow authority 會失效

## Failure modes 和最後規則

這個設計主要避免幾種 failure mode

### CLI / MCP drift

CLI 和 MCP 如果各自實作 review, 很快就會不一致

修法:

- CLI 和 MCP 都只呼叫 core
- review rule 只放一份
- queue state 只從 core 推導

### Wrapper logic leak

MCP wrapper 如果開始補 workflow rule, wrapper 會越來越厚

修法:

- MCP handler 只 decode request
- handler 呼叫 core
- handler format structured response
- 不在 handler 裡重新判斷 business rule

### Low-level tool bypass

low-level tools 太多, agent 會自己組 workflow

修法:

- public surface 收斂成 workflow-level tools
- response 帶足夠資訊
- review 和 sync-status 成為 official path

### Fuzzy completion

如果 completion 只是 `target file exists`, queue 會失真

修法:

- review / validation 要 pass
- source-language leftover 要清掉
- sync-status 要成功

### Duplicated state logic

如果 queue state 在 CLI, MCP, batch 各算一次, 結果會不一致

修法:

- queue state 推導集中在 core
- caller 只使用 core result

最後規則:

1. CLI 和 MCP 是 entrypoint, 不是兩個產品
2. `internal/core` 是唯一 workflow owner
3. MCP tool 要偏 workflow-level
4. Public surface 要少, response 要完整
5. Queue state 必須由系統推導
6. Completion 要有正式 path
7. Tool design 要防 agent bypass

這樣 CLI 和 MCP 才能共用同一個工具核心, 不會慢慢長成兩套系統

## References

1. [content-i18n GitHub Repository](https://github.com/loustack17/content-i18n)
2. [Model Context Protocol — What is MCP?](https://modelcontextprotocol.io/docs/getting-started/intro)
3. [Model Context Protocol — Architecture Overview](https://modelcontextprotocol.io/docs/learn/architecture)
4. [Model Context Protocol Specification — Basic Overview](https://modelcontextprotocol.io/specification/2025-06-18/basic/index)
5. [Model Context Protocol — SDKs](https://modelcontextprotocol.io/docs/sdk)
