内容
# 哦-我的-OOP
**RDD(责任驱动设计)工作坊的MCP服务器.**
引导用户进行用例 → 责任 → 协作 → 类图的设计流程,并为每种设计决策提供多个替代方案而非单一答案。基于乔英浩老师的《对象导论》和《面向对象编程的事实与误解》。
- 对象语言: **Kotlin** (不生成代码,支持设计建模)
- 存储位置:用户项目根目录下的 `.oop/*.md` (Markdown + YAML frontmatter + Mermaid)
- 变更可视化:Before/After Mermaid `classDiagram` 垂直堆栈 + 颜色强调
- 验证:责任适当性 / 耦合度 / 可测试性(均为建议,不阻止任何更改)
- 📚 **参考资料:**
- [课程/woowacourse-racingcar](lessons/woowacourse-racingcar/) — `woowacourse/kotlin-racingcar` 133个PR评审 ([SUMMARY](lessons/woowacourse-racingcar/SUMMARY.md))
- [课程/woowacourse-kotlin-lotto](lessons/woowacourse-kotlin-lotto/) — `woowacourse/kotlin-lotto` 141个PR评审(包含代码片段) ([SUMMARY](lessons/woowacourse-kotlin-lotto/SUMMARY.md))
---
## 快速安装 — AI助手指南
> **对AI说:** 如果用户要求安装此MCP,请按照以下步骤。所有命令均为非交互式,`<...>`表示实际路径/名称。
### 0. 前置条件
- **Node.js 18.17 或更高** (`node --version`)
- **git** (`git --version`)
如果缺少其中之一,请通知用户并停止。
### 1. 克隆和构建
```bash
git clone https://github.com/<OWNER>/Oh-My-OOP.git
cd Oh-My-OOP
npm install
npm run build
npm test
```
- `npm test` 应通过所有38个测试。
- 构建产物位于 `dist/index.js`。 **后续所有设置均使用此文件的绝对路径**。
安装后获取绝对路径:
```bash
# 从克隆目录:
echo "$(pwd)/dist/index.js"
```
将此输出值放入以下客户端设置中的 `<ABSOLUTE_PATH_TO_dist/index.js>`。
---
## 客户端注册
### Claude Code (CLI) — 用户范围(所有项目)
```bash
claude mcp add --scope user oh-my-oop -- node <ABSOLUTE_PATH_TO_dist/index.js>
```
确认:
```bash
claude mcp get oh-my-oop
# 状态:✓ Connected 表示成功
```
移除: `claude mcp remove oh-my-oop -s user`
### Claude Desktop (Mac/Windows)
配置文件位置:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
在 `mcpServers` 键中添加以下内容(保留现有项):
```json
{
"mcpServers": {
"oh-my-oop": {
"command": "node",
"args": ["<ABSOLUTE_PATH_TO_dist/index.js>"]
}
}
}
```
完全退出并重启Claude Desktop后,工具将出现在菜单中。
### Codex CLI (OpenAI)
在 `~/.codex/config.toml` 中添加以下部分:
```toml
[mcp_servers.oh-my-oop]
command = "node"
args = ["<ABSOLUTE_PATH_TO_dist/index.js>"]
```
重启Codex CLI后,通过 `/mcp` 验证注册。
### Cursor / 其他MCP主机
支持与Claude Desktop相同的JSON结构(`command` + `args`)。在主机MCP服务器设置位置添加相同项。
---
## 工作空间位置 — `.oop/` 在哪里生成
服务器按以下顺序确定工作目录:
1. 如果存在环境变量 `OOP_PROJECT_ROOT`,则使用其路径
2. 如果不存在,则使用服务器进程的cwd(由主机确定)
**如果要固定到项目级别**,请在客户端设置中通过环境变量指定:
**Claude Desktop / Cursor (JSON):**
```json
{
"oh-my-oop": {
"command": "node",
"args": ["<ABSOLUTE_PATH_TO_dist/index.js>"],
"env": {
"OOP_PROJECT_ROOT": "<ABSOLUTE_PATH_TO_USER_PROJECT>"
}
}
}
```
**Codex (TOML):**
```toml
[mcp_servers.oh-my-oop]
command = "node"
args = ["<ABSOLUTE_PATH_TO_dist/index.js>"]
env = { OOP_PROJECT_ROOT = "<ABSOLUTE_PATH_TO_USER_PROJECT>" }
```
**Claude Code (CLI):**
```bash
claude mcp add --scope user oh-my-oop -e OOP_PROJECT_ROOT=<ABSOLUTE_PATH_TO_USER_PROJECT> -- node <ABSOLUTE_PATH_TO_dist/index.js>
```
---
## 开始工作坊
在主机上调用 `rdd-workshop-intro` 提示后,指南将开始。
直接调用工具的流程:
```
1. oop_init({ project: "<项目名称>" })
2. oop_use_case_add({ title, actor, main_flow: [...] })
3. oop_propose_responsibilities({ use_case_id, n: 3 }) # 3个替代方案 + 权衡
4. oop_class_upsert({ name, stereotype, responsibilities, collaborators })
5. oop_collaboration_define({ from, to, message })
6. oop_diagram_generate({}) # 当前Mermaid classDiagram
7. oop_propose_alternatives({ question, context, n }) # 权衡引擎(通用)
8. oop_design_compare({ before, after }) # 前后颜色强调
9. oop_design_validate({}) # 始终建议,补救措施≥2
```
---
## 公开的MCP接口
### 工具(13个)
| 工具 | 副作用 | 避免单一答案 |
|---|---|---|
| `oop_init` | `.oop/` 初始化 | — |
| `oop_use_case_add` | `.oop/use-cases/<id>.md` | — |
| `oop_use_case_list` | 无 | — |
| `oop_propose_responsibilities` | 无 | ✅ 多个替代方案 |
| `oop_assign_responsibility` | CRC 更新 | — |
| `oop_class_upsert` | `.oop/classes/<id>.md` | — |
| `oop_class_list` | 无 | — |
| `oop_collaboration_define` | `.oop/collaborations/...md` | — |
| `oop_propose_alternatives` | 无 | ✅ 权衡引擎 |
| `oop_diagram_generate` | `.oop/diagrams/current.mmd` | — |
| `oop_design_compare` | 无 | — |
| `oop_design_validate` | 无 | ✅ 补救措施≥2 |
| `oop_state_read` | 无 | — |
### 资源
- `oop://design/index`, `oop://design/diagram`
- `oop://design/classes`, `oop://design/classes/{id}`
- `oop://design/use-cases`, `oop://design/use-cases/{id}`
- `oop://design/collaborations`
- `oop://design/history`, `oop://design/history/{file}`
### 提示
- `rdd-workshop-intro` — 工作坊开始 + 立体类型介绍
- `responsibility-discovery` — 用例 → CRC 草图多个方案
- `design-review-cho-younghos-lens` — 用乔英浩的视角审查设计
---
## 验证规则(15个,均保证补救措施≥2)
**基本10个:** `god-object`, `mixed-stereotype`, `low-cohesion`, `too-many-collaborators`, `feature-envy`, `non-newable`, `side-effect-in-holder`, `mocking-pressure`, `cycle`, `orphan-class`.
**额外5个** (通过优雅的Techcourse racingcar 133个PR分析提取):
- `vague-class-name` — 检测模糊的通用词汇,如Manager/Helper/Util/Validator
- `validation-misplacement` — UI/Coordinator/Domain之间的验证责任分散
- `dependency-direction` — Domain→Interfacer反向依赖(仅Coordinator允许)
- `data-source-duplication` — 两个卡片知道相同的领域信息,双方(分散真实来源)
- `function-not-object` — 没有成员,只有1个行为的类实际上是函数
阈值可通过 `.oop/design.md` frontmatter的 `thresholds` 重写:
```yaml
thresholds:
god_object_responsibilities: 7
god_object_collaborators: 6
cohesion_min_overlap: 0.2
too_many_collaborators: 4
mocking_pressure_max: 2
data_duplication_overlap: 0.5
```
详细设计原则见 [lessons/STYLE_GUIDE.md](lessons/STYLE_GUIDE.md)。
---
## 核心哲学(乔英浩老师的课程)
工具行为中嵌入的原则:
- **"设计不是找到正确答案,而是找到约束下的答案"**
→ 所有 `propose_*` 工具返回多个替代方案 + 权衡 + 乔英浩的视角(凝聚力/耦合度/可测试性)。
- **"代码控制权在开发者手中"**
→ `oop_design_validate` 始终建议,但不阻止任何更改。
- **"可测试性是好代码的标准"**
→ 通过 `non-newable`, `side-effect-in-holder`, `mocking-pressure` 规则衡量。
- **"如果mocking很多,那么代码不好"**
→ 通过 `mocking-pressure` 规则检测,并建议引入缝隙。
- **"Tell-Don't-Ask"**
→ `collaboration_shape` 种子的第一个选项,协作定义时的推荐模式。
---
## 目录结构(生成的 `.oop/`)
```
<project-root>/.oop/
├── design.md # 索引(frontmatter交叉引用)
├── use-cases/<id>.md
├── classes/<id>.md # CRC卡(knowing/doing + collaborators)
├── collaborations/<from>__<verb>__<to>.md
├── diagrams/current.mmd # 当前Mermaid classDiagram
└── history/<ISO>__<label>.mmd # 变更前快照
```
每个文件都是Markdown + YAML frontmatter,**人可以直接进行PR评审**。将 `.oop/` 提交到git,设计演变将保留在历史记录中。
---
## 问题解决
| 症状 | 原因 / 措施 |
|---|---|
| `Status: ✗ Failed to connect` | 确认 `node <path>` 是否直接执行。确认 `dist/index.js` 是否存在(是否构建)。 |
| `OOP_PROJECT_ROOT does not exist` | 环境变量指定的路径必须实际存在。使用绝对路径。 |
| 工具调用时 "Design index not found" | 必须先调用 `oop_init` 以创建 `.oop/`。 |
| Mermaid图表为空 | 添加类后必须调用 `oop_diagram_generate` 以更新 `current.mmd`。 |
| 中文ID损坏 | 可能是macOS的NFC/NFD差异。工具输入统一为NFC。不要直接修改文件名,只需使用工具修改。 |
---
## 开发
```bash
npm run dev # 使用tsx直接运行(开发中)
npm run build # tsc -> dist/
npm test # vitest 运行
npm run test:watch # vitest 监视
```
贡献时,所有 `propose_*` / `validate` 更改必须注意**"不提供单一答案"的约定**(通过 `assertHasMultipleRemedies` 强制执行,通过测试验证)。
---
## License
MIT.