manage-up-mcp 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# ManageUp MCP Server 本地安装与使用指南
|
|
2
|
+
|
|
3
|
+
本指南将帮助您在本地安装并配置 `ManageUp MCP Server`,使其能够被支持 MCP 协议的 AI 客户端(如 Claude Desktop, Cursor 等)调用,从而利用 `ManageUp` 的核心方法论生成高质量的职场报告。
|
|
4
|
+
|
|
5
|
+
## 1. 前提条件
|
|
6
|
+
|
|
7
|
+
在开始之前,请确保您的系统满足以下条件:
|
|
8
|
+
|
|
9
|
+
* **Node.js**: 版本 18 或更高。您可以通过在终端运行 `node -v` 来检查。
|
|
10
|
+
* **Git**: 用于克隆 `manage-up` 仓库。
|
|
11
|
+
* **AI 客户端**: 支持 MCP 协议的 AI 应用程序,例如 [Claude Desktop](https://www.anthropic.com/claude-desktop) 或 [Cursor](https://cursor.sh/)。
|
|
12
|
+
|
|
13
|
+
## 2. 安装步骤
|
|
14
|
+
|
|
15
|
+
请按照以下步骤在本地设置 `ManageUp MCP Server`:
|
|
16
|
+
|
|
17
|
+
### 步骤 2.1: 克隆 `manage-up` 仓库
|
|
18
|
+
|
|
19
|
+
如果您尚未克隆 `manage-up` 仓库,请打开终端并执行以下命令:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
git clone https://github.com/stevenchouai/manage-up.git
|
|
23
|
+
cd manage-up
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
如果您已经克隆了仓库,请确保您的本地仓库是最新的,并进入仓库目录:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
cd manage-up
|
|
30
|
+
git pull
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 步骤 2.2: 安装依赖
|
|
34
|
+
|
|
35
|
+
在 `manage-up` 仓库的根目录下,安装 MCP Server 所需的 Node.js 依赖:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 步骤 2.3: 本地验收
|
|
42
|
+
|
|
43
|
+
在接入 AI 客户端前,先运行一遍本地验证:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm run verify:mcp
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
这会做四件事:
|
|
50
|
+
|
|
51
|
+
1. 启动本地 MCP Server
|
|
52
|
+
2. 用真实 MCP Client 发现工具列表
|
|
53
|
+
3. 调用示例工具验证返回结果
|
|
54
|
+
4. 输出 telemetry 日志位置,确认调用数据是否成功落盘
|
|
55
|
+
|
|
56
|
+
### 步骤 2.4: 启动 MCP Server
|
|
57
|
+
|
|
58
|
+
`ManageUp MCP Server` 是一个基于 Stdio 传输的 Server。这意味着 AI 客户端会直接启动并管理 Server 进程。您无需手动运行一个长时间运行的进程。但是,为了验证 Server 是否能正常启动,您可以尝试运行:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm start
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
您应该会看到类似 `ManageUp MCP Server is running on stdio transport` 的输出,并在几秒后进程退出(因为没有 AI 客户端连接)。这表明 Server 已准备就绪。
|
|
65
|
+
|
|
66
|
+
## 3. npm 方式安装 / 分发
|
|
67
|
+
|
|
68
|
+
如果您希望把它当作一个标准 npm 工具使用,而不只是手动执行脚本,可以使用下面几种方式。
|
|
69
|
+
|
|
70
|
+
### 3.1 当前仓库本地安装
|
|
71
|
+
|
|
72
|
+
在仓库目录中:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm install
|
|
76
|
+
npm link
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
完成后,系统中会出现一个可执行命令:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
manage-up-mcp
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
这适合在您自己的电脑上长期使用。
|
|
86
|
+
|
|
87
|
+
### 3.2 打包后给其他人安装
|
|
88
|
+
|
|
89
|
+
先在本地打包:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm run pack:mcp
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
会生成类似:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
manage-up-mcp-1.1.0.tgz
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
其他人可以直接安装这个包:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install -g ./manage-up-mcp-1.1.0.tgz
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
这意味着:即使还没有发布到公共 npm,别人也可以通过 `.tgz` 文件安装。
|
|
108
|
+
|
|
109
|
+
### 3.3 发布到公共 npm(可选)
|
|
110
|
+
|
|
111
|
+
如果您希望别人能直接执行:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm install -g manage-up-mcp
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
那么还需要额外执行一次 `npm publish`。在发布前,请先确认包名、版本号、README 和 license 都已准备好。
|
|
118
|
+
|
|
119
|
+
## 4. 配置 AI 客户端
|
|
120
|
+
|
|
121
|
+
现在,您需要配置您的 AI 客户端来使用 `ManageUp MCP Server`。具体的配置方式可能因客户端而异,但核心思想是告诉客户端如何启动 `mcp-server.js` 文件。
|
|
122
|
+
|
|
123
|
+
### 4.1 针对 Claude Desktop / Cursor (推荐)
|
|
124
|
+
|
|
125
|
+
大多数支持 MCP 的 AI 客户端都提供了一个简单的配置界面来添加本地 MCP Server。您通常只需要提供 Server 的启动命令。
|
|
126
|
+
|
|
127
|
+
1. **打开您的 AI 客户端** (例如 Claude Desktop 或 Cursor)。
|
|
128
|
+
2. **导航到 MCP Server 配置界面**:这通常在“设置 (Settings)”、“扩展 (Extensions)”或“工具 (Tools)”部分。
|
|
129
|
+
3. **添加新的本地 Server**:
|
|
130
|
+
* **名称 (Name)**: `ManageUp MCP` (或您喜欢的任何名称)
|
|
131
|
+
* **命令 (Command)**: `manage-up-mcp`
|
|
132
|
+
* 如果您没有执行 `npm link`,也可以继续使用绝对路径方式:`node /path/to/your/manage-up/mcp-server.js`
|
|
133
|
+
|
|
134
|
+
4. **保存配置**。
|
|
135
|
+
|
|
136
|
+
配置完成后,您的 AI 客户端应该能够发现 `ManageUp MCP Server` 提供的工具,例如 `generate_weekly_report`、`generate_performance_review`、`generate_proposal` 和 `check_anti_fluff`。
|
|
137
|
+
|
|
138
|
+
### 4.2 使用 `manus-mcp-cli` (通用方式)
|
|
139
|
+
|
|
140
|
+
如果您的 AI 客户端支持通过 `manus-mcp-cli` 工具进行配置,您可以在终端中执行以下命令:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
manus-mcp-cli add --name "manage-up-mcp" --command "node /path/to/your/manage-up/mcp-server.js"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
同样,请将 `/path/to/your/manage-up/` 替换为您本地 `manage-up` 仓库的绝对路径。
|
|
147
|
+
|
|
148
|
+
如果您已经执行过 `npm link`,也可以改成:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
manus-mcp-cli add --name "manage-up-mcp" --command "manage-up-mcp"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 5. 如何使用
|
|
155
|
+
|
|
156
|
+
一旦配置成功,您就可以在 AI 客户端中通过自然语言调用 `ManageUp MCP Server` 提供的工具了。例如:
|
|
157
|
+
|
|
158
|
+
* **生成周报**: “帮我写一份周报,本周完成了用户登录模块开发,DAU 从 12000 涨到 15000,下周计划完成支付模块联调。”
|
|
159
|
+
* **生成绩效自评**: “写一份绩效自评,我主导完成了 X 项目,代码审查通过率 98%。”
|
|
160
|
+
* **检查空话**: “帮我检查一下这段话有没有空话:'本周团队积极推进各项工作,进展顺利。'”
|
|
161
|
+
|
|
162
|
+
AI 客户端会识别您的意图,并调用相应的 `ManageUp MCP` 工具来生成报告。
|
|
163
|
+
|
|
164
|
+
## 6. 监控与故障排查
|
|
165
|
+
|
|
166
|
+
`ManageUp MCP Server` 默认会记录本地 telemetry,用于监控工具是否被调用、是否成功、耗时多少。
|
|
167
|
+
|
|
168
|
+
### 默认采集内容
|
|
169
|
+
|
|
170
|
+
- `server_start`
|
|
171
|
+
- `tool_call_start`
|
|
172
|
+
- `tool_call_success`
|
|
173
|
+
- `tool_call_error`
|
|
174
|
+
- 工具名、时间戳、耗时、参数字段名和字段长度
|
|
175
|
+
|
|
176
|
+
默认**不会记录原始输入全文**,以减少敏感信息落盘风险。
|
|
177
|
+
|
|
178
|
+
### telemetry 日志位置
|
|
179
|
+
|
|
180
|
+
优先写入:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
~/.manage-up-mcp/logs/YYYY-MM-DD.jsonl
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
如果当前环境无法写入用户目录,则自动降级到当前工作目录下:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
.manage-up-mcp/logs/YYYY-MM-DD.jsonl
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 如需记录原始参数(谨慎)
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
MANAGE_UP_MCP_LOG_ARGS=1 manage-up-mcp
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 常见问题
|
|
199
|
+
|
|
200
|
+
* **Server 未启动**: 检查 Node.js 是否安装正确,`npm install` 是否成功,以及 `mcp-server.js` 的路径是否正确。
|
|
201
|
+
* **AI 客户端未发现工具**: 确保 MCP Server 已正确注册,并且 AI 客户端已重启或刷新。
|
|
202
|
+
* **工具调用失败**: 检查 AI 客户端的日志输出,通常会显示 MCP Server 返回的错误信息。
|
|
203
|
+
* **telemetry 没有落盘**: 先运行 `npm run verify:mcp`,查看输出中的 telemetry 路径;如果用户目录无权限,可手动设置 `MANAGE_UP_MCP_LOG_DIR`。
|
|
204
|
+
|
|
205
|
+
祝您使用愉快!
|
package/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# ManageUp
|
|
2
|
+
|
|
3
|
+
### 让大模型写出老板真正满意的报告
|
|
4
|
+
|
|
5
|
+
**[🇨🇳 中文](#manageup)** | **[🇺🇸 English](#english)**
|
|
6
|
+
|
|
7
|
+
> 大模型写的报告为什么不好?因为它没有你的数据,只能用空话填充。ManageUp 的核心思路:**先收集事实,再组织表达**。
|
|
8
|
+
|
|
9
|
+
ManageUp 是一个 AI Agent 技能仓库(Skill Warehouse),专门解决一个问题:**大模型写的职场报告太虚了**。
|
|
10
|
+
|
|
11
|
+
不只是工程师——产品经理、市场、销售、运营、HR,所有白领都要写报告给老板。但大模型写出来的报告充满了"积极推进"、"取得了一定进展"、"表现出色"这类空话。老板看完不知道你做了什么、做到了什么程度、需要他做什么。
|
|
12
|
+
|
|
13
|
+
ManageUp 通过一套反空话方法论 + 8 种职场沟通技能,教 AI 写出**有数据、有结论、有行动项**的报告、邮件和管理沟通材料。
|
|
14
|
+
|
|
15
|
+
## 核心方法论:五大原则
|
|
16
|
+
|
|
17
|
+
| 原则 | 核心理念 | 空话示例 | ManageUp 示例 |
|
|
18
|
+
|------|---------|---------|-------------|
|
|
19
|
+
| **BLUF 结论先行** | 第一句话就是结论 | "本周团队在多方面开展工作..." | "项目提前 2 天,但支付模块有阻塞项需决策" |
|
|
20
|
+
| **数据锚定** | 每个判断挂钩具体数据 | "取得了一定进展" | "完成率从 60% 提升至 85%" |
|
|
21
|
+
| **So-What 测试** | 每段话回答"老板看完会怎样" | "与产品团队沟通" | "与产品对齐了 SSO 方案,结论是用 OAuth 2.0" |
|
|
22
|
+
| **行动导向** | 明确老板需要做什么 | "请领导关注" | "需要周五前决策是否增加 1 名后端" |
|
|
23
|
+
| **校准语言** | 消灭模糊词 | "尽快"、"部分用户" | "3/25 前"、"12% 的付费用户(约 3,400 人)" |
|
|
24
|
+
|
|
25
|
+
## 技能列表
|
|
26
|
+
|
|
27
|
+
| 技能 | 用途 | 适用场景 |
|
|
28
|
+
|------|------|---------|
|
|
29
|
+
| [`manage-up-core`](skills/manage-up-core/SKILL.md) | 核心方法论 | 所有报告的基础 |
|
|
30
|
+
| [`weekly-report`](skills/weekly-report/SKILL.md) | 周报 / 月报 | 最高频的报告类型 |
|
|
31
|
+
| [`project-update`](skills/project-update/SKILL.md) | 项目进展汇报 | 含红绿灯状态和风险矩阵 |
|
|
32
|
+
| [`performance-review`](skills/performance-review/SKILL.md) | 绩效自评 / 述职 | 高风险,直接影响评级和晋升 |
|
|
33
|
+
| [`proposal`](skills/proposal/SKILL.md) | 提案 / 资源申请 | 含 ROI 分析和不行动的代价 |
|
|
34
|
+
| [`meeting-summary`](skills/meeting-summary/SKILL.md) | 会议纪要 | 聚焦决策和行动项 |
|
|
35
|
+
| [`quarterly-review`](skills/quarterly-review/SKILL.md) | 季度复盘 / QBR | 含记分卡和根因分析 |
|
|
36
|
+
| [`upward-email`](skills/upward-email/SKILL.md) | 向上汇报邮件 | 升级风险、催决策、会后跟进 |
|
|
37
|
+
| [`one-on-one-prep`](skills/one-on-one-prep/SKILL.md) | 1:1 沟通准备 | 资源争取、反馈、职业发展讨论 |
|
|
38
|
+
|
|
39
|
+
### 大厂汇报风格
|
|
40
|
+
|
|
41
|
+
| 技能 | 公司 | 核心框架 |
|
|
42
|
+
|------|------|---------|
|
|
43
|
+
| [`style-bytedance`](skills/style-bytedance/SKILL.md) | 字节跳动 | OKR 对齐、KR 进度追踪、飞书风格 |
|
|
44
|
+
| [`style-alibaba`](skills/style-alibaba/SKILL.md) | 阿里巴巴 | 双轨制(业绩 + 价值观)、述职、复盘 |
|
|
45
|
+
| [`style-tencent`](skills/style-tencent/SKILL.md) | 腾讯 | 产品指标驱动、数据先说、环比趋势 |
|
|
46
|
+
| [`style-google`](skills/style-google/SKILL.md) | Google | OKR 0.0-1.0 评分、GRAD、Impact 叙事 |
|
|
47
|
+
| [`style-amazon`](skills/style-amazon/SKILL.md) | Amazon | Leadership Principles、6-pager、Forte |
|
|
48
|
+
| [`style-microsoft`](skills/style-microsoft/SKILL.md) | Microsoft | Connects、Model/Coach/Care、Growth Mindset |
|
|
49
|
+
|
|
50
|
+
每个技能都包含:
|
|
51
|
+
- **SKILL.md** — 完整的技能指令(触发场景、输入模板、报告模板、反空话规则、质量检查清单)
|
|
52
|
+
- **examples.md** — 真实场景的输入/输出对比示例
|
|
53
|
+
|
|
54
|
+
## 安装
|
|
55
|
+
|
|
56
|
+
### Cursor(推荐)
|
|
57
|
+
|
|
58
|
+
将技能复制到 `~/.cursor/skills/` 目录(全局可用):
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
git clone https://github.com/stevenchouai/manage-up.git
|
|
62
|
+
cp -r manage-up/skills/* ~/.cursor/skills/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
或者只安装到当前项目:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/stevenchouai/manage-up.git
|
|
69
|
+
cp -r manage-up/skills/* .cursor/skills/
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Claude Code
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
mkdir -p ~/.claude/skills
|
|
76
|
+
git clone https://github.com/stevenchouai/manage-up.git
|
|
77
|
+
cp -r manage-up/skills/* ~/.claude/skills/
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### OpenAI Codex CLI
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
mkdir -p ~/.codex/skills
|
|
84
|
+
git clone https://github.com/stevenchouai/manage-up.git
|
|
85
|
+
cp -r manage-up/skills/* ~/.codex/skills/
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 其他 AI 编辑器
|
|
89
|
+
|
|
90
|
+
ManageUp 技能基于 Agent Skills 开放标准(SKILL.md),理论上兼容所有支持该标准的工具。将 `skills/` 目录下的文件夹复制到对应工具的技能目录即可。
|
|
91
|
+
|
|
92
|
+
详细安装指南见 [docs/install.md](docs/install.md)。
|
|
93
|
+
|
|
94
|
+
## 使用方法
|
|
95
|
+
|
|
96
|
+
安装后,只需用自然语言告诉 AI 你要写什么报告:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
帮我写周报,这周做了这些事:
|
|
100
|
+
- 完成了登录模块重构
|
|
101
|
+
- 修了 8 个 bug
|
|
102
|
+
- DAU 从 12000 涨到 14500
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
AI 会自动激活对应的 ManageUp 技能,先问你补充必要的数据,再生成结构化的报告。
|
|
106
|
+
|
|
107
|
+
如果你直接提供了足够的信息,AI 会直接生成报告而不再追问。
|
|
108
|
+
|
|
109
|
+
如果你想先快速感受差别,再决定是否安装,先看 [docs/showcase.md](docs/showcase.md)。
|
|
110
|
+
|
|
111
|
+
## 为什么大模型写的报告不好?
|
|
112
|
+
|
|
113
|
+
| 问题 | 原因 | ManageUp 的解法 |
|
|
114
|
+
|------|------|----------------|
|
|
115
|
+
| 空话太多 | AI 没有你的数据,只能用模糊词填充 | 强制先收集数据再写作 |
|
|
116
|
+
| 没有结构 | AI 不知道老板想看什么格式 | 每种报告有专业模板 |
|
|
117
|
+
| 不知道重点 | AI 把所有事情平铺直叙 | BLUF 原则:结论先行 |
|
|
118
|
+
| 缺少行动项 | AI 只描述不建议 | 每份报告必须有决策请求 |
|
|
119
|
+
| 语言虚浮 | AI 偏爱华丽辞藻 | 禁用词表 + 校准语言规则 |
|
|
120
|
+
|
|
121
|
+
## 适用人群
|
|
122
|
+
|
|
123
|
+
ManageUp 不只是给工程师用的。所有需要向上沟通的人都可以用:
|
|
124
|
+
|
|
125
|
+
- 工程师 / 技术负责人
|
|
126
|
+
- 产品经理
|
|
127
|
+
- 市场 / 运营
|
|
128
|
+
- 销售
|
|
129
|
+
- 项目经理
|
|
130
|
+
- HR / 行政
|
|
131
|
+
- 任何需要向上汇报、发管理邮件或准备 1:1 的白领
|
|
132
|
+
|
|
133
|
+
## 贡献
|
|
134
|
+
|
|
135
|
+
欢迎提交 Pull Request 贡献新的报告技能或改进现有技能。贡献指南:
|
|
136
|
+
|
|
137
|
+
1. 每个技能一个文件夹,包含 `SKILL.md` 和 `examples.md`
|
|
138
|
+
2. 遵循 `manage-up-core` 的五大原则
|
|
139
|
+
3. 必须包含反空话规则和质量检查清单
|
|
140
|
+
4. 示例必须包含"空话版 vs ManageUp 版"的对比
|
|
141
|
+
|
|
142
|
+
开始贡献前,建议先阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 和 [docs/quality-roadmap.md](docs/quality-roadmap.md),并运行:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
./scripts/validate-skills.sh
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
详细规范见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
|
149
|
+
|
|
150
|
+
## 质量保障
|
|
151
|
+
|
|
152
|
+
仓库现在包含一套基础质量门禁:
|
|
153
|
+
|
|
154
|
+
- `bash scripts/validate-skills.sh` 会检查每个 skill 是否包含必需文件和关键章节
|
|
155
|
+
- GitHub Actions 会在 push / pull request 时自动运行校验
|
|
156
|
+
- `manage-up-core` 现在也包含示例,便于理解方法论本身如何落地
|
|
157
|
+
|
|
158
|
+
如果你准备新增 skill,建议先跑一次:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
bash scripts/validate-skills.sh
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
如果你想先快速感受效果,再决定是否安装,先看 [docs/showcase.md](docs/showcase.md)。
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
<a name="english"></a>
|
|
173
|
+
|
|
174
|
+
## English
|
|
175
|
+
|
|
176
|
+
### Make AI write reports your boss actually wants to read
|
|
177
|
+
|
|
178
|
+
ManageUp is an AI Agent Skill Warehouse that solves one problem: **LLM-generated workplace reports are too vague and generic**.
|
|
179
|
+
|
|
180
|
+
Every white-collar professional — engineers, product managers, marketers, salespeople, operations — writes reports for their boss. But AI-generated reports are filled with fluff like "made good progress", "demonstrated strong leadership", and "will continue to drive growth". The boss finishes reading and doesn't know what you did, how well you did it, or what they need to do.
|
|
181
|
+
|
|
182
|
+
ManageUp provides an **anti-fluff methodology + 8 workplace communication skills** that teach AI to write reports, emails, and manager-facing materials with **real data, clear conclusions, and actionable items**.
|
|
183
|
+
|
|
184
|
+
### Skills
|
|
185
|
+
|
|
186
|
+
| Skill | Purpose |
|
|
187
|
+
|-------|---------|
|
|
188
|
+
| [`manage-up-core`](skills/manage-up-core/SKILL.md) | Core methodology (5 anti-fluff principles) |
|
|
189
|
+
| [`weekly-report`](skills/weekly-report/SKILL.md) | Weekly / Monthly status reports |
|
|
190
|
+
| [`project-update`](skills/project-update/SKILL.md) | Project progress with RAG status |
|
|
191
|
+
| [`performance-review`](skills/performance-review/SKILL.md) | Performance self-assessments |
|
|
192
|
+
| [`proposal`](skills/proposal/SKILL.md) | Proposals & resource requests with ROI |
|
|
193
|
+
| [`meeting-summary`](skills/meeting-summary/SKILL.md) | Meeting minutes focused on decisions & action items |
|
|
194
|
+
| [`quarterly-review`](skills/quarterly-review/SKILL.md) | Quarterly business reviews (QBR) |
|
|
195
|
+
| [`upward-email`](skills/upward-email/SKILL.md) | Upward status and escalation emails |
|
|
196
|
+
| [`one-on-one-prep`](skills/one-on-one-prep/SKILL.md) | 1:1 prep with managers and skip-levels |
|
|
197
|
+
|
|
198
|
+
#### Company Reporting Styles
|
|
199
|
+
|
|
200
|
+
| Skill | Company | Core Framework |
|
|
201
|
+
|-------|---------|---------------|
|
|
202
|
+
| [`style-bytedance`](skills/style-bytedance/SKILL.md) | ByteDance | OKR alignment, KR tracking, Lark style |
|
|
203
|
+
| [`style-alibaba`](skills/style-alibaba/SKILL.md) | Alibaba | Dual-track (KPI + Values), promotion reviews |
|
|
204
|
+
| [`style-tencent`](skills/style-tencent/SKILL.md) | Tencent | Product-metric-driven, data-first, WoW trends |
|
|
205
|
+
| [`style-google`](skills/style-google/SKILL.md) | Google | OKR 0-1.0 scoring, GRAD, impact narratives |
|
|
206
|
+
| [`style-amazon`](skills/style-amazon/SKILL.md) | Amazon | Leadership Principles, 6-pager, Forte reviews |
|
|
207
|
+
| [`style-microsoft`](skills/style-microsoft/SKILL.md) | Microsoft | Connects, Model/Coach/Care, growth mindset |
|
|
208
|
+
|
|
209
|
+
### Quick Install (Cursor)
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
git clone https://github.com/stevenchouai/manage-up.git
|
|
213
|
+
cp -r manage-up/skills/* ~/.cursor/skills/
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
For other editors, see [docs/install.md](docs/install.md).
|
package/mcp-server.js
ADDED
|
@@ -0,0 +1,765 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
12
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
+
import {
|
|
14
|
+
CallToolRequestSchema,
|
|
15
|
+
ListToolsRequestSchema,
|
|
16
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
17
|
+
|
|
18
|
+
const SERVER_NAME = "manage-up-mcp";
|
|
19
|
+
const SERVER_VERSION = "1.1.0";
|
|
20
|
+
const DEFAULT_LOG_DIR = path.join(os.homedir(), ".manage-up-mcp", "logs");
|
|
21
|
+
const FALLBACK_LOG_DIR = path.join(process.cwd(), ".manage-up-mcp", "logs");
|
|
22
|
+
const SESSION_ID = randomUUID();
|
|
23
|
+
const LOG_RAW_ARGS = process.env.MANAGE_UP_MCP_LOG_ARGS === "1";
|
|
24
|
+
|
|
25
|
+
const antiFluffRules = {
|
|
26
|
+
"取得了一定进展": "完成率从 X% 提升至 Y%",
|
|
27
|
+
"进展顺利": "按计划完成 N 项里程碑中的 M 项",
|
|
28
|
+
"显著提升": "提升了 X%(从 A 到 B)",
|
|
29
|
+
"有所改善": "指标从 X 变为 Y",
|
|
30
|
+
"大量工作": "完成了 N 个任务/处理了 N 个工单",
|
|
31
|
+
"各方面表现良好": "在 X、Y、Z 三个维度达标",
|
|
32
|
+
"积极推进": "完成了哪项动作、产出了什么结果、影响了什么指标",
|
|
33
|
+
"持续跟进": "本周完成了什么,下周还剩什么,预计何时完成",
|
|
34
|
+
"深入讨论": "对齐了什么结论、谁负责下一步、截止时间是什么",
|
|
35
|
+
"尽快": "在具体日期前",
|
|
36
|
+
"部分用户": "影响了多少用户或占比多少",
|
|
37
|
+
"风险较高": "P1/P2 风险 + 量化影响 + 触发条件",
|
|
38
|
+
"资源紧张": "缺少多少人力/预算,当前缺口造成什么影响",
|
|
39
|
+
"made good progress": "completed N of M milestones",
|
|
40
|
+
"significant improvement": "improved from X to Y (Z% improvement)",
|
|
41
|
+
"worked on various tasks": "delivered A, B, and C",
|
|
42
|
+
"productive discussion": "aligned on X; next step is Y by DATE",
|
|
43
|
+
"positive feedback": "received N positive responses / score moved from X to Y",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const weeklyReportSchema = z.object({
|
|
47
|
+
report_type: z.enum(["weekly", "monthly"]).default("weekly"),
|
|
48
|
+
completed_items: z.string().min(1, "completed_items 不能为空"),
|
|
49
|
+
metrics: z.string().optional(),
|
|
50
|
+
blockers: z.string().optional(),
|
|
51
|
+
next_period_plan: z.string().min(1, "next_period_plan 不能为空"),
|
|
52
|
+
decisions_needed: z.string().optional(),
|
|
53
|
+
language: z.enum(["zh", "en"]).default("zh"),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const performanceReviewSchema = z.object({
|
|
57
|
+
achievements: z.string().min(1, "achievements 不能为空"),
|
|
58
|
+
metrics: z.string().min(1, "metrics 不能为空"),
|
|
59
|
+
growth_areas: z.string().optional(),
|
|
60
|
+
challenges: z.string().optional(),
|
|
61
|
+
language: z.enum(["zh", "en"]).default("zh"),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const proposalSchema = z.object({
|
|
65
|
+
proposal_title: z.string().min(1, "proposal_title 不能为空"),
|
|
66
|
+
problem_statement: z.string().min(1, "problem_statement 不能为空"),
|
|
67
|
+
proposed_solution: z.string().min(1, "proposed_solution 不能为空"),
|
|
68
|
+
expected_benefits: z.string().optional(),
|
|
69
|
+
resource_required: z.string().optional(),
|
|
70
|
+
timeline: z.string().optional(),
|
|
71
|
+
language: z.enum(["zh", "en"]).default("zh"),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const antiFluffSchema = z.object({
|
|
75
|
+
text: z.string().min(1, "text 不能为空"),
|
|
76
|
+
language: z.enum(["zh", "en"]).default("zh"),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const tools = [
|
|
80
|
+
{
|
|
81
|
+
name: "generate_weekly_report",
|
|
82
|
+
description:
|
|
83
|
+
"生成数据驱动的周报/月报,遵循 ManageUp 核心原则。结论先行、禁止空话、明确老板需要做什么。",
|
|
84
|
+
inputSchema: {
|
|
85
|
+
type: "object",
|
|
86
|
+
properties: {
|
|
87
|
+
report_type: {
|
|
88
|
+
type: "string",
|
|
89
|
+
enum: ["weekly", "monthly"],
|
|
90
|
+
default: "weekly",
|
|
91
|
+
description: "报告类型:weekly (周报) 或 monthly (月报)",
|
|
92
|
+
},
|
|
93
|
+
completed_items: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description:
|
|
96
|
+
"本周/月完成的具体事项和产出。可用分号、换行或列表分隔。",
|
|
97
|
+
},
|
|
98
|
+
metrics: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description:
|
|
101
|
+
"关键数据指标的变化。例:DAU 从 12000 涨到 15000;代码覆盖率从 65% 提升到 78%",
|
|
102
|
+
},
|
|
103
|
+
blockers: {
|
|
104
|
+
type: "string",
|
|
105
|
+
description:
|
|
106
|
+
"遇到的风险、阻塞项及需要老板提供的帮助。例:支付接口对接延迟,等待第三方 API 文档,预计影响上线时间 3 天",
|
|
107
|
+
},
|
|
108
|
+
next_period_plan: {
|
|
109
|
+
type: "string",
|
|
110
|
+
description:
|
|
111
|
+
"下周/月最重要的 2-3 件事及预计完成日期。可用分号、换行或列表分隔。",
|
|
112
|
+
},
|
|
113
|
+
decisions_needed: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description:
|
|
116
|
+
"需要老板做的决策、截止日期及不决策的后果。",
|
|
117
|
+
},
|
|
118
|
+
language: {
|
|
119
|
+
type: "string",
|
|
120
|
+
enum: ["zh", "en"],
|
|
121
|
+
default: "zh",
|
|
122
|
+
description: "输出语言:zh (中文) 或 en (英文)",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
required: ["completed_items", "next_period_plan"],
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "generate_performance_review",
|
|
130
|
+
description:
|
|
131
|
+
"生成数据驱动的绩效自评,突出具体成就和量化指标,避免空话。",
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: "object",
|
|
134
|
+
properties: {
|
|
135
|
+
achievements: {
|
|
136
|
+
type: "string",
|
|
137
|
+
description: "本周期的主要成就。可用分号、换行或列表分隔。",
|
|
138
|
+
},
|
|
139
|
+
metrics: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "量化指标。例:线上 bug 率下降 30%,团队效率提升 25%",
|
|
142
|
+
},
|
|
143
|
+
growth_areas: {
|
|
144
|
+
type: "string",
|
|
145
|
+
description: "成长领域。",
|
|
146
|
+
},
|
|
147
|
+
challenges: {
|
|
148
|
+
type: "string",
|
|
149
|
+
description: "遇到的挑战及解决方案。",
|
|
150
|
+
},
|
|
151
|
+
language: {
|
|
152
|
+
type: "string",
|
|
153
|
+
enum: ["zh", "en"],
|
|
154
|
+
default: "zh",
|
|
155
|
+
description: "输出语言",
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
required: ["achievements", "metrics"],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "generate_proposal",
|
|
163
|
+
description:
|
|
164
|
+
"生成行动导向的提案/资源申请,清晰说明需求、预期收益和决策截止。",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
type: "object",
|
|
167
|
+
properties: {
|
|
168
|
+
proposal_title: {
|
|
169
|
+
type: "string",
|
|
170
|
+
description: "提案标题。",
|
|
171
|
+
},
|
|
172
|
+
problem_statement: {
|
|
173
|
+
type: "string",
|
|
174
|
+
description: "问题陈述。",
|
|
175
|
+
},
|
|
176
|
+
proposed_solution: {
|
|
177
|
+
type: "string",
|
|
178
|
+
description: "建议方案。",
|
|
179
|
+
},
|
|
180
|
+
expected_benefits: {
|
|
181
|
+
type: "string",
|
|
182
|
+
description: "预期收益或 ROI。",
|
|
183
|
+
},
|
|
184
|
+
resource_required: {
|
|
185
|
+
type: "string",
|
|
186
|
+
description: "所需资源。",
|
|
187
|
+
},
|
|
188
|
+
timeline: {
|
|
189
|
+
type: "string",
|
|
190
|
+
description: "时间线和决策截止。",
|
|
191
|
+
},
|
|
192
|
+
language: {
|
|
193
|
+
type: "string",
|
|
194
|
+
enum: ["zh", "en"],
|
|
195
|
+
default: "zh",
|
|
196
|
+
description: "输出语言",
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
required: ["proposal_title", "problem_statement", "proposed_solution"],
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
name: "check_anti_fluff",
|
|
204
|
+
description:
|
|
205
|
+
"检查文本中是否存在 ManageUp 禁用的模糊词汇,并给出改进建议。",
|
|
206
|
+
inputSchema: {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties: {
|
|
209
|
+
text: {
|
|
210
|
+
type: "string",
|
|
211
|
+
description: "需要检查的文本内容",
|
|
212
|
+
},
|
|
213
|
+
language: {
|
|
214
|
+
type: "string",
|
|
215
|
+
enum: ["zh", "en"],
|
|
216
|
+
default: "zh",
|
|
217
|
+
description: "文本语言",
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
required: ["text"],
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
];
|
|
224
|
+
|
|
225
|
+
const server = new Server(
|
|
226
|
+
{
|
|
227
|
+
name: SERVER_NAME,
|
|
228
|
+
version: SERVER_VERSION,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
capabilities: {
|
|
232
|
+
tools: {},
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
238
|
+
return { tools };
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
242
|
+
const { name, arguments: rawArgs } = request.params;
|
|
243
|
+
const startedAt = Date.now();
|
|
244
|
+
|
|
245
|
+
emitTelemetry("tool_call_start", {
|
|
246
|
+
toolName: name,
|
|
247
|
+
argsSummary: summarizeArgs(rawArgs),
|
|
248
|
+
rawArgs: LOG_RAW_ARGS ? rawArgs ?? null : undefined,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
let result;
|
|
253
|
+
|
|
254
|
+
switch (name) {
|
|
255
|
+
case "generate_weekly_report":
|
|
256
|
+
result = handleWeeklyReport(rawArgs ?? {});
|
|
257
|
+
break;
|
|
258
|
+
case "generate_performance_review":
|
|
259
|
+
result = handlePerformanceReview(rawArgs ?? {});
|
|
260
|
+
break;
|
|
261
|
+
case "generate_proposal":
|
|
262
|
+
result = handleProposal(rawArgs ?? {});
|
|
263
|
+
break;
|
|
264
|
+
case "check_anti_fluff":
|
|
265
|
+
result = handleAntiFluffCheck(rawArgs ?? {});
|
|
266
|
+
break;
|
|
267
|
+
default:
|
|
268
|
+
result = toolError(`Tool not found: ${name}`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
emitTelemetry(result.isError ? "tool_call_error" : "tool_call_success", {
|
|
272
|
+
toolName: name,
|
|
273
|
+
durationMs: Date.now() - startedAt,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
return result;
|
|
277
|
+
} catch (error) {
|
|
278
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
279
|
+
|
|
280
|
+
emitTelemetry("tool_call_error", {
|
|
281
|
+
toolName: name,
|
|
282
|
+
durationMs: Date.now() - startedAt,
|
|
283
|
+
error: message,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
return toolError(message);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
function handleWeeklyReport(rawArgs) {
|
|
291
|
+
const parsed = weeklyReportSchema.safeParse(rawArgs);
|
|
292
|
+
if (!parsed.success) {
|
|
293
|
+
return validationError(parsed.error);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const {
|
|
297
|
+
report_type: reportType,
|
|
298
|
+
completed_items: completedItems,
|
|
299
|
+
metrics,
|
|
300
|
+
blockers,
|
|
301
|
+
next_period_plan: nextPeriodPlan,
|
|
302
|
+
decisions_needed: decisionsNeeded,
|
|
303
|
+
language,
|
|
304
|
+
} = parsed.data;
|
|
305
|
+
|
|
306
|
+
const completedList = parseItems(completedItems);
|
|
307
|
+
const metricsList = parseItems(metrics);
|
|
308
|
+
const blockerList = parseItems(blockers);
|
|
309
|
+
const planList = parseItems(nextPeriodPlan);
|
|
310
|
+
const decisionList = parseItems(decisionsNeeded);
|
|
311
|
+
|
|
312
|
+
const isMonthly = reportType === "monthly";
|
|
313
|
+
const nextPeriodLabel = language === "en" ? (isMonthly ? "Next Month" : "Next Week") : (isMonthly ? "下月" : "下周");
|
|
314
|
+
const header = language === "en" ? `${isMonthly ? "Monthly" : "Weekly"} Report` : `${isMonthly ? "月报" : "周报"}`;
|
|
315
|
+
const summary = buildWeeklySummary({
|
|
316
|
+
language,
|
|
317
|
+
completedList,
|
|
318
|
+
blockerList,
|
|
319
|
+
decisionList,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const body =
|
|
323
|
+
language === "en"
|
|
324
|
+
? [
|
|
325
|
+
`# ${header}`,
|
|
326
|
+
"",
|
|
327
|
+
"## Bottom Line",
|
|
328
|
+
summary,
|
|
329
|
+
"",
|
|
330
|
+
"## Key Metrics",
|
|
331
|
+
formatSectionList(metricsList, "No metrics provided."),
|
|
332
|
+
"",
|
|
333
|
+
"## Completed",
|
|
334
|
+
formatSectionList(completedList, "No completed items provided."),
|
|
335
|
+
"",
|
|
336
|
+
"## Risks & Blockers",
|
|
337
|
+
formatSectionList(
|
|
338
|
+
blockerList,
|
|
339
|
+
"No major blockers reported. FYI only."
|
|
340
|
+
),
|
|
341
|
+
"",
|
|
342
|
+
`## ${nextPeriodLabel}`,
|
|
343
|
+
formatSectionList(planList, "No plan provided."),
|
|
344
|
+
"",
|
|
345
|
+
"## Decisions Needed",
|
|
346
|
+
formatSectionList(decisionList, "No decisions needed at this time."),
|
|
347
|
+
"",
|
|
348
|
+
"---",
|
|
349
|
+
"*Generated by ManageUp MCP*",
|
|
350
|
+
].join("\n")
|
|
351
|
+
: [
|
|
352
|
+
`# ${header}`,
|
|
353
|
+
"",
|
|
354
|
+
"## 核心结论",
|
|
355
|
+
summary,
|
|
356
|
+
"",
|
|
357
|
+
"## 关键指标",
|
|
358
|
+
formatSectionList(metricsList, "未提供指标数据。"),
|
|
359
|
+
"",
|
|
360
|
+
"## 本周期完成",
|
|
361
|
+
formatSectionList(completedList, "未提供完成项。"),
|
|
362
|
+
"",
|
|
363
|
+
"## 风险与阻塞",
|
|
364
|
+
formatSectionList(blockerList, "当前无明显阻塞,本报告仅供知悉。"),
|
|
365
|
+
"",
|
|
366
|
+
`## ${nextPeriodLabel}计划`,
|
|
367
|
+
formatSectionList(planList, "未提供计划。"),
|
|
368
|
+
"",
|
|
369
|
+
"## 需要决策",
|
|
370
|
+
formatSectionList(decisionList, "当前无需老板决策。"),
|
|
371
|
+
"",
|
|
372
|
+
"---",
|
|
373
|
+
"*由 ManageUp MCP 生成*",
|
|
374
|
+
].join("\n");
|
|
375
|
+
|
|
376
|
+
return textResult(body);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function handlePerformanceReview(rawArgs) {
|
|
380
|
+
const parsed = performanceReviewSchema.safeParse(rawArgs);
|
|
381
|
+
if (!parsed.success) {
|
|
382
|
+
return validationError(parsed.error);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const { achievements, metrics, growth_areas: growthAreas, challenges, language } = parsed.data;
|
|
386
|
+
|
|
387
|
+
const achievementList = parseItems(achievements);
|
|
388
|
+
const metricsList = parseItems(metrics);
|
|
389
|
+
const growthList = parseItems(growthAreas);
|
|
390
|
+
const challengeList = parseItems(challenges);
|
|
391
|
+
|
|
392
|
+
const summary =
|
|
393
|
+
language === "en"
|
|
394
|
+
? `The strongest evidence of impact this period is ${pickFirst(metricsList, "the quantified outcomes delivered against plan")}.`
|
|
395
|
+
: `本周期最能证明价值的证据是:${pickFirst(metricsList, "已交付的量化结果")}。`;
|
|
396
|
+
|
|
397
|
+
const body =
|
|
398
|
+
language === "en"
|
|
399
|
+
? [
|
|
400
|
+
"# Performance Review",
|
|
401
|
+
"",
|
|
402
|
+
"## Executive Summary",
|
|
403
|
+
summary,
|
|
404
|
+
"",
|
|
405
|
+
"## Key Achievements",
|
|
406
|
+
formatSectionList(achievementList, "No achievements provided."),
|
|
407
|
+
"",
|
|
408
|
+
"## Quantified Metrics",
|
|
409
|
+
formatSectionList(metricsList, "No metrics provided."),
|
|
410
|
+
"",
|
|
411
|
+
"## Growth Areas",
|
|
412
|
+
formatSectionList(
|
|
413
|
+
growthList,
|
|
414
|
+
"No additional growth notes provided."
|
|
415
|
+
),
|
|
416
|
+
"",
|
|
417
|
+
"## Challenges & Solutions",
|
|
418
|
+
formatSectionList(
|
|
419
|
+
challengeList,
|
|
420
|
+
"No major challenges documented."
|
|
421
|
+
),
|
|
422
|
+
"",
|
|
423
|
+
"---",
|
|
424
|
+
"*Generated by ManageUp MCP*",
|
|
425
|
+
].join("\n")
|
|
426
|
+
: [
|
|
427
|
+
"# 绩效自评",
|
|
428
|
+
"",
|
|
429
|
+
"## 核心总结",
|
|
430
|
+
summary,
|
|
431
|
+
"",
|
|
432
|
+
"## 主要成就",
|
|
433
|
+
formatSectionList(achievementList, "未提供主要成就。"),
|
|
434
|
+
"",
|
|
435
|
+
"## 量化指标",
|
|
436
|
+
formatSectionList(metricsList, "未提供量化指标。"),
|
|
437
|
+
"",
|
|
438
|
+
"## 成长与提升",
|
|
439
|
+
formatSectionList(growthList, "未补充成长信息。"),
|
|
440
|
+
"",
|
|
441
|
+
"## 挑战与解决方案",
|
|
442
|
+
formatSectionList(challengeList, "未补充挑战信息。"),
|
|
443
|
+
"",
|
|
444
|
+
"---",
|
|
445
|
+
"*由 ManageUp MCP 生成*",
|
|
446
|
+
].join("\n");
|
|
447
|
+
|
|
448
|
+
return textResult(body);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function handleProposal(rawArgs) {
|
|
452
|
+
const parsed = proposalSchema.safeParse(rawArgs);
|
|
453
|
+
if (!parsed.success) {
|
|
454
|
+
return validationError(parsed.error);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const {
|
|
458
|
+
proposal_title: proposalTitle,
|
|
459
|
+
problem_statement: problemStatement,
|
|
460
|
+
proposed_solution: proposedSolution,
|
|
461
|
+
expected_benefits: expectedBenefits,
|
|
462
|
+
resource_required: resourceRequired,
|
|
463
|
+
timeline,
|
|
464
|
+
language,
|
|
465
|
+
} = parsed.data;
|
|
466
|
+
|
|
467
|
+
const benefitsList = parseItems(expectedBenefits);
|
|
468
|
+
const resourceList = parseItems(resourceRequired);
|
|
469
|
+
const timelineList = parseItems(timeline);
|
|
470
|
+
|
|
471
|
+
const summary =
|
|
472
|
+
language === "en"
|
|
473
|
+
? `Recommendation: approve "${proposalTitle}" to address ${firstSentence(problemStatement)}`
|
|
474
|
+
: `建议批准“${proposalTitle}”,用于解决:${firstSentence(problemStatement)}`;
|
|
475
|
+
|
|
476
|
+
const body =
|
|
477
|
+
language === "en"
|
|
478
|
+
? [
|
|
479
|
+
`# Proposal: ${proposalTitle}`,
|
|
480
|
+
"",
|
|
481
|
+
"## Executive Summary",
|
|
482
|
+
summary,
|
|
483
|
+
"",
|
|
484
|
+
"## Problem Statement",
|
|
485
|
+
problemStatement,
|
|
486
|
+
"",
|
|
487
|
+
"## Proposed Solution",
|
|
488
|
+
proposedSolution,
|
|
489
|
+
"",
|
|
490
|
+
"## Expected Benefits / ROI",
|
|
491
|
+
formatSectionList(
|
|
492
|
+
benefitsList,
|
|
493
|
+
"Benefits not provided yet. Add time/cost/quality impact for approval readiness."
|
|
494
|
+
),
|
|
495
|
+
"",
|
|
496
|
+
"## Resources Required",
|
|
497
|
+
formatSectionList(resourceList, "Resources still to be confirmed."),
|
|
498
|
+
"",
|
|
499
|
+
"## Timeline & Decision Deadline",
|
|
500
|
+
formatSectionList(timelineList, "Decision timing not provided."),
|
|
501
|
+
"",
|
|
502
|
+
"---",
|
|
503
|
+
"*Generated by ManageUp MCP*",
|
|
504
|
+
].join("\n")
|
|
505
|
+
: [
|
|
506
|
+
`# 提案:${proposalTitle}`,
|
|
507
|
+
"",
|
|
508
|
+
"## 执行摘要",
|
|
509
|
+
summary,
|
|
510
|
+
"",
|
|
511
|
+
"## 问题陈述",
|
|
512
|
+
problemStatement,
|
|
513
|
+
"",
|
|
514
|
+
"## 建议方案",
|
|
515
|
+
proposedSolution,
|
|
516
|
+
"",
|
|
517
|
+
"## 预期收益 / ROI",
|
|
518
|
+
formatSectionList(
|
|
519
|
+
benefitsList,
|
|
520
|
+
"暂未提供收益数据,建议补充时间、成本或质量收益。"
|
|
521
|
+
),
|
|
522
|
+
"",
|
|
523
|
+
"## 所需资源",
|
|
524
|
+
formatSectionList(resourceList, "资源需求待补充。"),
|
|
525
|
+
"",
|
|
526
|
+
"## 时间线与决策截止",
|
|
527
|
+
formatSectionList(timelineList, "暂未提供时间线。"),
|
|
528
|
+
"",
|
|
529
|
+
"---",
|
|
530
|
+
"*由 ManageUp MCP 生成*",
|
|
531
|
+
].join("\n");
|
|
532
|
+
|
|
533
|
+
return textResult(body);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function handleAntiFluffCheck(rawArgs) {
|
|
537
|
+
const parsed = antiFluffSchema.safeParse(rawArgs);
|
|
538
|
+
if (!parsed.success) {
|
|
539
|
+
return validationError(parsed.error);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const { text, language } = parsed.data;
|
|
543
|
+
const suggestions = [];
|
|
544
|
+
|
|
545
|
+
for (const [fluff, replacement] of Object.entries(antiFluffRules)) {
|
|
546
|
+
if (text.includes(fluff)) {
|
|
547
|
+
suggestions.push(
|
|
548
|
+
language === "en"
|
|
549
|
+
? `"${fluff}" -> replace with "${replacement}"`
|
|
550
|
+
: `"${fluff}" -> 建议改为 "${replacement}"`
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const body =
|
|
556
|
+
language === "en"
|
|
557
|
+
? [
|
|
558
|
+
"# Anti-Fluff Check",
|
|
559
|
+
"",
|
|
560
|
+
`## Issues Found: ${suggestions.length}`,
|
|
561
|
+
"",
|
|
562
|
+
suggestions.length > 0
|
|
563
|
+
? suggestions.map((item) => `- ${item}`).join("\n")
|
|
564
|
+
: "No vague phrases detected.",
|
|
565
|
+
"",
|
|
566
|
+
"## Recommendation",
|
|
567
|
+
suggestions.length > 0
|
|
568
|
+
? "Rewrite each vague statement with a concrete deliverable, metric, owner, or deadline."
|
|
569
|
+
: "The wording is already fairly concrete. Keep checking for missing metrics or decisions.",
|
|
570
|
+
"",
|
|
571
|
+
"---",
|
|
572
|
+
"*Generated by ManageUp MCP*",
|
|
573
|
+
].join("\n")
|
|
574
|
+
: [
|
|
575
|
+
"# 反空话检查报告",
|
|
576
|
+
"",
|
|
577
|
+
`## 发现问题数:${suggestions.length}`,
|
|
578
|
+
"",
|
|
579
|
+
suggestions.length > 0
|
|
580
|
+
? suggestions.map((item) => `- ${item}`).join("\n")
|
|
581
|
+
: "未发现明显模糊表达。",
|
|
582
|
+
"",
|
|
583
|
+
"## 修改建议",
|
|
584
|
+
suggestions.length > 0
|
|
585
|
+
? "把模糊表达改成具体交付物、数据、责任人或截止时间。"
|
|
586
|
+
: "措辞已经比较具体,下一步重点检查是否还缺数字或决策信息。",
|
|
587
|
+
"",
|
|
588
|
+
"---",
|
|
589
|
+
"*由 ManageUp MCP 生成*",
|
|
590
|
+
].join("\n");
|
|
591
|
+
|
|
592
|
+
return textResult(body);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function parseItems(value) {
|
|
596
|
+
if (!value) {
|
|
597
|
+
return [];
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return value
|
|
601
|
+
.split(/\n|;|;/)
|
|
602
|
+
.map((item) => item.replace(/^[-*•\d.\s]+/, "").trim())
|
|
603
|
+
.filter(Boolean);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function formatSectionList(items, emptyText) {
|
|
607
|
+
if (items.length === 0) {
|
|
608
|
+
return emptyText;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return items.map((item) => `- ${item}`).join("\n");
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function buildWeeklySummary({ language, completedList, blockerList, decisionList }) {
|
|
615
|
+
if (language === "en") {
|
|
616
|
+
if (decisionList.length > 0) {
|
|
617
|
+
return `Key update: ${pickFirst(completedList, "major work was delivered")} and a decision is now needed on ${decisionList[0]}.`;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (blockerList.length > 0) {
|
|
621
|
+
return `Key update: ${pickFirst(completedList, "major work was delivered")}, but the main current risk is ${blockerList[0]}.`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return `FYI: ${pickFirst(completedList, "the planned work was delivered")} and no major blocker or decision is currently open.`;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (decisionList.length > 0) {
|
|
628
|
+
return `核心更新:${pickFirst(completedList, "本周期关键工作已完成")};当前需要老板决策的是 ${decisionList[0]}。`;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (blockerList.length > 0) {
|
|
632
|
+
return `核心更新:${pickFirst(completedList, "本周期关键工作已完成")};当前最大风险是 ${blockerList[0]}。`;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return `仅供知悉:${pickFirst(completedList, "本周期关键工作已按计划推进")},当前没有需要升级的阻塞或决策事项。`;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function pickFirst(items, fallback) {
|
|
639
|
+
return items.length > 0 ? items[0] : fallback;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
function firstSentence(text) {
|
|
643
|
+
return text.split(/[\n。.!?]/).map((part) => part.trim()).find(Boolean) ?? text;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
function textResult(text) {
|
|
647
|
+
return {
|
|
648
|
+
content: [
|
|
649
|
+
{
|
|
650
|
+
type: "text",
|
|
651
|
+
text,
|
|
652
|
+
},
|
|
653
|
+
],
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function toolError(message) {
|
|
658
|
+
return {
|
|
659
|
+
content: [
|
|
660
|
+
{
|
|
661
|
+
type: "text",
|
|
662
|
+
text: `Error: ${message}`,
|
|
663
|
+
},
|
|
664
|
+
],
|
|
665
|
+
isError: true,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function validationError(error) {
|
|
670
|
+
return toolError(
|
|
671
|
+
error.issues
|
|
672
|
+
.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`)
|
|
673
|
+
.join("; ")
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function summarizeArgs(args) {
|
|
678
|
+
if (!args || typeof args !== "object") {
|
|
679
|
+
return {
|
|
680
|
+
keys: [],
|
|
681
|
+
fieldCount: 0,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const entries = Object.entries(args);
|
|
686
|
+
|
|
687
|
+
return {
|
|
688
|
+
keys: entries.map(([key]) => key),
|
|
689
|
+
fieldCount: entries.length,
|
|
690
|
+
valueLengths: Object.fromEntries(
|
|
691
|
+
entries.map(([key, value]) => [key, String(value ?? "").length])
|
|
692
|
+
),
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function emitTelemetry(eventType, payload = {}) {
|
|
697
|
+
const logDir = resolveLogDir();
|
|
698
|
+
const record = {
|
|
699
|
+
timestamp: new Date().toISOString(),
|
|
700
|
+
eventType,
|
|
701
|
+
sessionId: SESSION_ID,
|
|
702
|
+
serverName: SERVER_NAME,
|
|
703
|
+
serverVersion: SERVER_VERSION,
|
|
704
|
+
pid: process.pid,
|
|
705
|
+
...payload,
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
const line = JSON.stringify(record);
|
|
709
|
+
console.error(`[TELEMETRY] ${line}`);
|
|
710
|
+
|
|
711
|
+
try {
|
|
712
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
713
|
+
const file = path.join(logDir, `${new Date().toISOString().slice(0, 10)}.jsonl`);
|
|
714
|
+
fs.appendFileSync(file, `${line}\n`, "utf8");
|
|
715
|
+
} catch (error) {
|
|
716
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
717
|
+
console.error(`[TELEMETRY_WRITE_ERROR] ${message}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
export async function startServer() {
|
|
722
|
+
const logDir = resolveLogDir();
|
|
723
|
+
emitTelemetry("server_start", {
|
|
724
|
+
logDir,
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
const transport = new StdioServerTransport();
|
|
728
|
+
await server.connect(transport);
|
|
729
|
+
console.error(
|
|
730
|
+
`ManageUp MCP Server is running on stdio transport (session ${SESSION_ID})`
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function resolveLogDir() {
|
|
735
|
+
const requestedDir = process.env.MANAGE_UP_MCP_LOG_DIR || DEFAULT_LOG_DIR;
|
|
736
|
+
|
|
737
|
+
if (canWriteDirectory(requestedDir)) {
|
|
738
|
+
return requestedDir;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
return FALLBACK_LOG_DIR;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function canWriteDirectory(dirPath) {
|
|
745
|
+
try {
|
|
746
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
747
|
+
fs.accessSync(dirPath, fs.constants.W_OK);
|
|
748
|
+
return true;
|
|
749
|
+
} catch {
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const isMainModule =
|
|
755
|
+
process.argv[1] &&
|
|
756
|
+
path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
757
|
+
|
|
758
|
+
if (isMainModule) {
|
|
759
|
+
startServer().catch((error) => {
|
|
760
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
761
|
+
emitTelemetry("server_fatal", { error: message });
|
|
762
|
+
console.error("Fatal error:", error);
|
|
763
|
+
process.exit(1);
|
|
764
|
+
});
|
|
765
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "manage-up-mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "ManageUp MCP Server - Transform workplace reports with data-driven, conclusion-first methodology",
|
|
5
|
+
"main": "mcp-server.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"bin/",
|
|
9
|
+
"mcp-server.js",
|
|
10
|
+
"README.md",
|
|
11
|
+
"ManageUp MCP Server 本地安装与使用指南.md"
|
|
12
|
+
],
|
|
13
|
+
"bin": {
|
|
14
|
+
"manage-up-mcp": "./bin/manage-up-mcp.js"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"mcp": "node mcp-server.js",
|
|
18
|
+
"start": "node ./bin/manage-up-mcp.js",
|
|
19
|
+
"verify:mcp": "node scripts/test-mcp-server.js",
|
|
20
|
+
"pack:mcp": "npm pack --cache ./.npm-cache"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"mcp",
|
|
27
|
+
"manage-up",
|
|
28
|
+
"workplace-reports",
|
|
29
|
+
"ai-agent"
|
|
30
|
+
],
|
|
31
|
+
"author": "ManageUp Team",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
35
|
+
"zod": "^4.3.6"
|
|
36
|
+
}
|
|
37
|
+
}
|