内容
# MS Project MCP
一款 [Model Context Protocol](https://modelcontextprotocol.io) 服务器,为 AI 助手提供对 **Microsoft Project 桌面应用程序** 的完全读取和写入访问权限,通过 COM 自动化(与 VBA 宏使用的相同机制)驱动。AI 助手可以执行在 Project 中可以完成的所有操作。
它连接到一个正在运行的 **Microsoft Project** 实例(或启动一个),因此不会进行文件解析,也不会损失保真度 —— 真实的调度引擎会完成工作。
---
## 要求
该服务器 **必须在安装了 Microsoft Project 的 Windows 机器上运行**。它不能在 macOS/Linux 或没有安装 Project 的机器上运行,因为它与实际应用程序进行通信。
- Windows 10/11
- Microsoft Project(桌面版)—— 推荐 Project 2016 或更新版本
- Python 3.10+
- Python 包:`mcp`、`pywin32`
## 安装(在 Project 机器上)
```powershell
cd <此文件夹>
python -m pip install -r requirements.txt
# 或:python -m pip install -e .
```
## 首先测试 COM 层
在将其连接到 AI 客户端之前,请单独确认 COM 自动化是否有效:
```powershell
# 连接 + 创建一个空白项目:
python scripts\smoke_com.py
# 连接 + 打开一个真实计划并读取其第一个任务:
python scripts\smoke_com.py "C:\path\to\your\plan.mpp"
```
如果打印任务行,则说明难点部分有效。如果出现错误,则问题出在 COM/Project 上,而不是 MCP —— 请参见下面的**故障排除**。
## 注册到 MCP 客户端
**Claude Desktop** —— 添加到 `claude_desktop_config.json`:
```json
{
"mcpServers": {
"ms-project": {
"command": "python",
"args": ["-m", "ms_project_mcp.server"],
"cwd": "C:\\path\\to\\this\\folder"
}
}
}
```
**Claude Code** —— `claude mcp add ms-project -- python -m ms_project_mcp.server`
(从此文件夹运行,或传递 `--cwd`)。
然后要求助手调用 `health_check`。
---
## 建议的第一次对话(第 1 阶段门槛)
1. **health_check** → 确认版本 + 连接
2. **open_project**,使用真实 `.mpp` 的绝对路径
3. **get_project_info** → 标题、日期、任务/资源计数
4. **get_tasks**(尝试 `only_critical: true`)→ 读取调度
5. **get_progress_summary** → 已完成 / 已逾期 / 里程碑
如果这五个操作针对真实计划有效,则第 1 阶段已验证,第 2 阶段(写入任务)已解锁。
---
## 架构
```
ms_project_mcp/
server.py # FastMCP 服务器;注册每个工具模块
com/
connection.py # 专用的 STA 工作线程,拥有 Application 对象
constants.py # 验证的 MS Project 枚举值(单一真实来源)
helpers.py # 日期/持续时间转换 + 任务/资源序列化
tools/
session.py # 打开/新建/保存/关闭,项目信息,多项目(第 1 阶段)
tasks_read.py # 列出/获取/搜索任务,进度摘要(第 1 阶段)
... # tasks_write,dependencies,resources,calendars,
# scheduling,tracking,custom_fields,io,...(第 2-5 阶段)
scripts/
smoke_com.py # 仅限 COM 的烟雾测试(无 MCP)
```
**为什么使用专用的 COM 线程?** Microsoft Project 是一个单线程公寓(STA)COM 服务器 —— COM 指针不能在线程之间共享。FastMCP 在线程池上运行工具,因此 `connection.py` 将所有 COM 工作映射到一个专用的线程,该线程拥有 `Application` 对象。这避免了 `RPC_E_WRONG_THREAD` 错误,并自然地序列化访问(Project 无法处理并发调用)。
**添加工具:** 在 `register(mcp)` 内的相关 `tools/*.py` 模块中添加一个函数,用 `with_app(...)` 或 `with_project(...)` 包装 COM 工作,并返回一个 JSON 友好的字典。将任何神奇数字放入 `com/constants.py`。
---
## 工具目录 —— 110 个工具
**会话和文件(10):** `health_check`、`open_project`、`new_project`、`get_project_info`、`set_project_properties`、`save_project`、`save_project_as`、`close_project`、`list_projects`、`switch_project`
**任务读取(4):** `get_tasks`、`get_task`、`search_tasks`、`get_progress_summary`
**任务写入(22):** `add_task`、`bulk_add_tasks`、`update_task`、`bulk_update_tasks`、`delete_task`、`set_task_mode`、`bulk_set_task_mode`、`set_constraint`、`clear_constraint`、`set_deadline`、`bulk_set_deadlines`、`set_task_active`、`indent_task`、`set_milestone`、`set_percent_complete`、`set_task_notes`、`set_task_hyperlink`、`set_task_calendar`、`move_task`、`copy_task_structure`、`add_recurring_task`、`insert_subproject`
**依赖关系(5):** `add_dependency`、`bulk_add_dependencies`、`remove_dependency`、`get_task_dependencies`、`get_dependency_chain`
**资源(16):** `get_resources`、`get_resource`、`add_resource`、`update_resource`、`delete_resource`、`set_resource_calendar`、`assign_resource`、`bulk_assign_resources`、`remove_resource_assignment`、`get_task_assignments`、`get_resource_assignments`、`find_overallocated_resources`、`get_resource_workload`、`get_resource_availability`、`get_resource_rate_tables`、`set_resource_rate_table`
**日历(9):** `get_calendars`、`create_calendar`、`delete_calendar`、`set_project_calendar`、`set_calendar_exception`、`list_calendar_exceptions`、`delete_calendar_exception`、`set_working_hours`、`get_working_week`
*(`delete_calendar` 和 `set_project_calendar` 报告一个 COM 限制 —— 请参见注释。)*
**调度和分析(13):** `get_critical_path`、`get_schedule_analysis`、`find_available_slack`、`get_constraints`、`validate_schedule`、`calculate_project`、`set_calculation_mode`、`get_milestone_report`、`get_overdue_tasks`、`get_tasks_by_resource`、`level_resources`、`clear_leveling`、`what_if_delay`
**跟踪和挣值(12):** `save_baseline`、`clear_baseline`、`compare_baselines`、`get_variance_report`、`get_earned_value`、`get_cost_summary`、`get_actual_work`、`get_progress_by_wbs`、`get_timephased_data`、`set_status_date`、`update_progress`、`reschedule_incomplete_work`
**自定义字段(6):** `get_custom_field`、`get_custom_field_values`、`set_custom_field`、`update_custom_fields`、`bulk_set_custom_field`、`rename_custom_field`
**导入/导出(4):** `snapshot_to_json`、`snapshot_diff`、`export_csv`、`export_xml`
**过滤和结构(5):** `filter_tasks`、`group_tasks_by`、`get_wbs_structure`、`get_tasks_by_rag`、`apply_filter`
**实用程序(4):** `undo_last`、`set_undo_levels`、`get_settings`、`dry_run_bulk_update`
> 这超过了 ~79 工具开源 COM 参考,该项目启发了该项目。
---
## 限制和配置
- **COM 超时** —— 每个操作等待 **300 秒** 的 Microsoft Project,然后返回错误而不是永远挂起服务器。通常,主机上的 *模态对话框*(覆盖或“保存更改?”提示)会阻塞单个 COM 线程 —— 取消它并重试。可以通过 `MSPROJECT_MCP_COM_TIMEOUT` 环境变量覆盖(秒);格式错误的数值将被忽略。
- **结果上限** —— 浏览/报告工具在未给出明确的 `limit` 时,将其行列表限制在 **1000**,并在有更多匹配时设置 `"truncated": true`,以便大型计划不会溢出客户端/模型上下文。涵盖 `get_tasks`、`filter_tasks`、每个任务的跟踪报告(`get_earned_value`、`get_variance_report`、`compare_baselines`、`get_cost_summary`、`get_actual_work`、`get_progress_by_wbs`)和 `validate_schedule`(其 `summary` 计数保持准确)。传递 `limit` 或过滤器以获取更多。
- **保证有效 JSON** —— 非有限浮点数(`NaN`/`Infinity`,MS Project 可以为 SPI/CPI/百分比字段生成,当分母为零时)被剥离为 `null`,因为这些令牌是无效 JSON 并破坏严格的客户端。
- **批量写入隔离失败** —— `bulk_*` 工具不再在单个错误项目上中止整个批次;失败被收集在项目下的 `"errors"` 键下。
## 故障排除/首次运行验证
所有 110 个工具都是根据官方 MS Project VBA 参考(枚举值和方法签名已针对 learn.microsoft.com 验证)编写的,但 **仅通过语法检查验证** —— 它们尚未针对实时 Project 实例运行。所有 COM 方法都使用 **位置** 参数调用(win32com 延迟绑定不可靠地接受关键字参数)。一些 COM 调用因 Project 版本而异或依赖于活动视图;如果一个表现异常,这些是可能的地点(每个都是孤立的,通常是单行修复):
| 症状 | 位置 | 可能的修复 |
|---|---|---|
| `new_project` 弹出模板对话框或错误 | `session.new_project` | 使用 `app.FileNew(Template="")` |
| `close_project` 在 `FileCloseEx` 上出错 | `session.close_project` | 某些版本使用 `app.FileClose(0)` |
| `switch_project` 在 `.Activate()` 上出错 | `session.switch_project` | 通过 `app.Windows` 激活 |
| 项目开始/结束时间为空 | `session.get_project_info` | 属性可能是 `proj.Start`/`proj.Finish` |
| `get_task` 按 unique_id 失败 | `helpers.find_task` | `Tasks.UniqueID(n)` 访问器名称 |
| `move_task`/`copy_task_structure` 粘贴到错误行 | `tasks_write` | 行 = 视图位置,而非 ID;基于选择的 & 脆弱设计 |
| 日历异常保存为工作时间,而您想要非工作时间 | `calendars.set_calendar_exception` | 确认无偏移的异常在您的版本中默认为非工作时间 |
| `save_baseline` 到基准 1-10 在 `MISSING` 参数上出错 | `tracking.save_baseline` | 罕见;如果 `pythoncom.Missing` 被拒绝为 `Copy` 插槽,请传递 `PjSaveBaselineFrom` 整数 |
| `assign_resource` 附加到错误的资源 | `resources.py` | 确认 `Assignments.Add` 需要行 `.ID`(此处使用,根据 VBA 文档)而非 `UniqueID` 在您的版本上 |
| `set_working_hours` / 日历异常时间被拒绝 | `calendars.py` | 如果 `"8:00 AM"` 字符串未被强制转换,请将偏移 `Start`/`Finish` 转换为 `datetime` |
| 间歇性 `RPC_E_WRONG_THREAD` | `com/connection.py` | STA 工作线程应防止这种情况发生 —— 如果出现,请报告 |
运行 `scripts\smoke_com.py`(和 `pytest tests/`)以确定哪些(如果有)需要调整,并粘贴错误给我 —— 每个调整都是一次快速更改。
> **注意:** AI 控制实时 Project 应用程序。它可见地运行,因此您可以观看。使用 `save_project` 需要谨慎;除非您保存,否则不会写入磁盘。