内容
# OneNote 组织工具 MCP 服务器
一款由 AI 驱动的模型上下文协议(MCP)服务器,允许 Claude 等 AI 助手发现、读取和重新组织您的个人 Microsoft OneNote 笔记本。
## 背景
管理个人 OneNote 笔记本往往会随着时间的推移而变得杂乱无章 — 页面被随意添加到通用章节,命名变得不一致,查找信息变得更加困难。该 MCP 服务器弥合了 AI 助手和 OneNote API 之间的差距,使笔记本对话式组织成为可能。
服务器提供了一组工具,允许 AI 助手:
- **浏览**您的笔记本层次结构(笔记本 → 章节 → 页面)
- **读取**页面内容进行分析
- **重新组织**,通过移动页面,重命名它们,或执行批量计划
- **预览**所有更改,在提交前进行 dry-run
它使用 Microsoft Graph 进行 OneNote 访问,通过设备代码流进行身份验证(无需存储密码),并遵循安全优先的理念:所有破坏性操作都支持 dry-run 预览,并且完全记录。
## 架构
```mermaid
graph TD
subgraph "AI 助手(Claude Desktop / Cursor / VS Code)"
Client[MCP 客户端]
end
subgraph "MCP 服务器进程"
Transport["传输层<br/>stdio / HTTP+SSE"]
Server[FastMCP 服务器]
Tools[工具实现]
Auth[身份验证模块<br/>设备代码流]
Graph[图形客户端<br/>httpx 异步]
Logger[操作日志记录器]
end
subgraph "外部服务"
AAD[Microsoft Entra ID<br/>OAuth 2.0]
MSGraph[Microsoft Graph API<br/>OneNote 端点]
end
Client <-->|JSON-RPC via stdio| Transport
Transport <--> Server
Server --> Tools
Tools --> Auth
Tools --> Graph
Tools --> Logger
Auth -->|设备代码流| AAD
Graph -->|REST + 持有者令牌| MSGraph
Auth -.->|get_access_token| Graph
```
### 分层架构
| 层 | 责任 | 关键技术 |
|-------|---------------|----------|
| 传输 | stdio 或 HTTP/SSE 通信 | FastMCP |
| 工具 | 输入验证,编排,响应格式化 | Python 异步 |
| 图形客户端 | 分页,错误映射,REST 调用构造 | httpx |
| 身份验证 | 设备代码流,令牌缓存,静默刷新 | MSAL + Fernet |
| 日志记录器 | 所有写入操作的结构化审计日志记录 | 标准库 |
## 序列图 — 页面移动
```mermaid
sequenceDiagram
participant User as 用户(Claude Desktop)
participant AI as Claude AI
participant MCP as MCP 服务器
participant Graph as Microsoft Graph
User->>AI: "将我的会议笔记移动到工作部分"
AI->>MCP: move_page_to_section(pageId, targetSectionId, dryRun=true)
MCP->>Graph: GET /pages/{id} (元数据)
Graph-->>MCP: 页面标题,当前章节
MCP->>Graph: GET /sections/{id} (源和目标名称)
Graph-->>MCP: 章节显示名称
MCP-->>AI: {success: true, dryRun: true, summary: "将'会议笔记'从'收件箱'移动到'工作'"}
AI->>User: "我会将'会议笔记'从收件箱移动到工作。你需要吗?"
User->>AI: "是的,请执行"
AI->>MCP: move_page_to_section(pageId, targetSectionId, dryRun=false)
MCP->>Graph: POST /pages/{id}/copyToSection
Graph-->>MCP: 操作 URL (202 Accepted)
MCP->>Graph: GET 操作 URL (poll)
Graph-->>MCP: 状态:已完成
MCP-->>AI: {success: true, summary: "将'会议笔记'从'收件箱'移动到'工作'"}
AI->>User: "完成!将'会议笔记'移动到工作部分。"
```
## 项目结构
```
onenote_organizer/
├── __init__.py # 包标记
├── __main__.py # CLI 入口点(传输选择)
├── server.py # FastMCP 服务器 + 所有 8 个工具实现
├── auth.py # 身份验证提供者协议 + 设备代码身份验证提供者(MSAL)
├── graph_client.py # 异步 Microsoft Graph 客户端(httpx)
├── logger.py # 结构化操作日志记录
└── models.py # 冻结的数据类 + 自定义异常
```
## 先决条件
- **Python 3.11+** (适用于 macOS,Windows 和 Linux)
- **Azure 应用注册** (参见下面的设置指南)
### 平台兼容性
| 平台 | 状态 | Python 命令 | 虚拟环境激活 |
|----------|--------|---------------|-----------------|
| macOS | ✅ 完全支持 | `python3` | `source .venv/bin/activate` |
| Windows | ✅ 完全支持 | `python` | `.venv\Scripts\activate` |
| Linux | ✅ 完全支持 | `python3` | `source .venv/bin/activate` |
## 设置 Azure 应用注册
按照以下步骤在 Azure 门户中创建所需的应用注册:
### 步骤 1:创建应用注册
1. 进入 [Azure 门户](https://portal.azure.com) → **Microsoft Entra ID** → **应用注册**
2. 点击 **新建注册**
3. 填写:
- **名称**:`OneNote Organizer MCP` (或您喜欢的任何名称)
- **支持的帐户类型**:选择 "任何组织目录和个人 Microsoft 帐户"
- **重定向 URI**:留空(设备代码流不需要)
4. 点击 **注册**
5. 在概览页面上,复制 **应用程序(客户端)ID** — 这是您的 `AZURE_CLIENT_ID`
### 步骤 2:配置 API 权限
1. 在您的应用注册中,进入 **API 权限** → **添加权限**
2. 选择 **Microsoft Graph** → **委托权限**
3. 搜索并添加:
- `Notes.Read`
- `Notes.ReadWrite`
4. 点击 **添加权限**
5. (可选)如果使用组织帐户,请点击 **授予管理员同意** — 对于个人帐户,不需要此步骤
### 步骤 3:启用公共客户端流
1. 进入 **身份验证** 在左侧边栏
2. 滚动到 **高级设置**
3. 将 **允许公共客户端流** 设置为 **是**
4. 点击 **保存**
### 步骤 4:记录您的配置
您现在拥有所有必要的信息:
| 设置 | 在哪里找到它 | 用作 |
|---------|------------------|---------|
| 应用程序(客户端)ID | 应用注册 → 概览 | `AZURE_CLIENT_ID` |
| 目录(租户)ID | 应用注册 → 概览 | `AZURE_TENANT_ID` (可选) |
> **个人 Microsoft 帐户**:使用 `AZURE_TENANT_ID=common` (默认值)。这涵盖了 @outlook.com,@hotmail.com,@live.com 帐户。
>
> **工作/学校帐户**:使用您的组织的租户 ID 从概览页面。
## 安装
### macOS
```bash
cd /path/to/OneNoteMcp
# 创建虚拟环境
python3 -m venv .venv
source .venv/bin/activate
# 安装包
pip install -e .
```
### Windows
```powershell
cd C:\path\to\OneNoteMcp
# 创建虚拟环境
python -m venv .venv
.venv\Scripts\activate
# 安装包
pip install -e .
```
> **注意**:在 Windows 上,请使用 `python` (而非 `python3`)。确保 Python 3.11+ 已在您的 PATH 中。
## 环境变量
| 变量 | 必需 | 默认值 | 描述 |
|----------|----------|---------|-------------|
| `AZURE_CLIENT_ID` | 是 | — | 您的 Azure 应用注册客户端 ID |
| `AZURE_TENANT_ID` | 否 | `common` | Azure AD 租户(对个人 Microsoft 帐户使用 `common`) |
| `ONENOTE_LOG_DESTINATION` | 否 | `stderr` | 日志输出:文件路径,`stdout` 或 `stderr` |
## 运行服务器
### macOS
```bash
# 激活虚拟环境
source /path/to/OneNoteMcp/.venv/bin/activate
# 在 stdio 模式下运行(默认 — 用于 Claude Desktop)
python -m onenote_organizer
# 或使用已安装的控制台脚本
onenote-organizer
# HTTP/SSE 模式(适用于远程客户端)
python -m onenote_organizer --transport http --port 8080
```
### Windows
```powershell
# 激活虚拟环境
C:\path\to\OneNoteMcp\.venv\Scripts\activate
# 在 stdio 模式下运行(默认 — 用于 Claude Desktop)
python -m onenote_organizer
# 或使用已安装的控制台脚本
onenote-organizer
# HTTP/SSE 模式(适用于远程客户端)
python -m onenote_organizer --transport http --port 8080
```
## 从 Claude Desktop 连接
### macOS
**配置文件位置**:`~/Library/Application Support/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"onenote-organizer": {
"command": "/path/to/OneNoteMcp/.venv/bin/python",
"args": ["-m", "onenote_organizer"],
"env": {
"AZURE_CLIENT_ID": "your-azure-client-id-here",
"AZURE_TENANT_ID": "common"
}
}
}
}
```
**示例** (使用真实路径):
```json
{
"mcpServers": {
"onenote-organizer": {
"command": "/Users/yourname/Projects/OneNoteMcp/.venv/bin/python",
"args": ["-m", "onenote_organizer"],
"env": {
"AZURE_CLIENT_ID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"AZURE_TENANT_ID": "common"
}
}
}
}
```
**查看 MCP 日志**:
```bash
tail -f ~/Library/Logs/Claude/mcp-server-onenote-organizer.log
```
### Windows
**配置文件位置**:`%APPDATA%\Claude\claude_desktop_config.json`
通常:`C:\Users\YourName\AppData\Roaming\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"onenote-organizer": {
"command": "C:\\path\\to\\OneNoteMcp\\.venv\\Scripts\\python.exe",
"args": ["-m", "onenote_organizer"],
"env": {
"AZURE_CLIENT_ID": "your-azure-client-id-here",
"AZURE_TENANT_ID": "common"
}
}
}
}
```
**示例** (使用真实路径):
```json
{
"mcpServers": {
"onenote-organizer": {
"command": "C:\\Users\\YourName\\Projects\\OneNoteMcp\\.venv\\Scripts\\python.exe",
"args": ["-m", "onenote_organizer"],
"env": {
"AZURE_CLIENT_ID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"AZURE_TENANT_ID": "common"
}
}
}
}
```
> **Windows 上的重要事项**:在 JSON 路径中使用双反斜杠 (`\\`),并指向 `.venv\Scripts` 中的 `python.exe`。
**查看 MCP 日志**:检查 `%APPDATA%\Claude\Logs\` 中的日志文件。
### 配置后(两个平台)
1. **保存** 配置文件
2. **重启 Claude Desktop** 完全(退出并重新打开)
3. 查找 **锤子图标** ()在聊天中 - 它显示可用的 MCP 工具
4. **首次使用**:当您调用任何工具时,服务器启动设备代码身份验证:
- 在 MCP 日志中查找设备代码和 URL
- 访问 https://microsoft.com/devicelogin 并输入代码
- 使用您的 Microsoft 帐户登录并授予 OneNote 权限
- 服务器缓存加密的刷新令牌以备将来会话使用
### 替代:使用控制台脚本
安装 `pip install -e .` 后,您可以直接引用脚本:
**macOS**:
```json
{
"mcpServers": {
"onenote-organizer": {
"command": "/path/to/OneNoteMcp/.venv/bin/onenote-organizer",
"env": { "AZURE_CLIENT_ID": "your-client-id" }
}
}
}
```
**Windows**:
```json
{
"mcpServers": {
"onenote-organizer": {
"command": "C:\\path\\to\\OneNoteMcp\\.venv\\Scripts\\onenote-organizer.exe",
"env": { "AZURE_CLIENT_ID": "your-client-id" }
}
}
}
```
## 连接其他客户端
### Cursor(macOS)
添加 `.cursor/mcp.json`:
```json
{
"mcpServers": {
"onenote-organizer": {
"command": "/path/to/OneNoteMcp/.venv/bin/python",
"args": ["-m", "onenote_organizer"],
"env": {
"AZURE_CLIENT_ID": "your-azure-client-id-here"
}
}
}
}
```
### Cursor(Windows)
```json
{
"mcpServers": {
"onenote-organizer": {
"command": "C:\\path\\to\\OneNoteMcp\\.venv\\Scripts\\python.exe",
"args": ["-m", "onenote_organizer"],
"env": {
"AZURE_CLIENT_ID": "your-azure-client-id-here"
}
}
}
}
```
### VS Code / Copilot(macOS)
添加 `.vscode/mcp.json`:
```json
{
"servers": {
"onenote-organizer": {
"command": "/path/to/OneNoteMcp/.venv/bin/python",
"args": ["-m", "onenote_organizer"],
"env": {
"AZURE_CLIENT_ID": "your-azure-client-id-here"
}
}
}
}
```
### VS Code / Copilot(Windows)
```json
{
"servers": {
"onenote-organizer": {
"command": "C:\\path\\to\\OneNoteMcp\\.venv\\Scripts\\python.exe",
"args": ["-m", "onenote_organizer"],
"env": {
"AZURE_CLIENT_ID": "your-azure-client-id-here"
}
}
}
}
```
## 可用工具
| 工具 | 描述 | 输入 |
|------|-------------|--------|
| `list_notebooks` | 列出所有 OneNote 笔记本 | — |
| `list_section_groups` | 列出笔记本中的章节组(文件夹) | `notebook_id` |
| `list_sections` | 列出笔记本中的章节 | `notebook_id` |
| `list_pages` | 列出章节中的页面 | `section_id` |
| `get_page_content` | 读取页面内容 | `page_id`, `format` (html/text) |
| `create_section_group` | 在笔记本中创建章节组(文件夹) | `notebook_id`, `display_name` |
| `create_section_in_group` | 在章节组中创建章节 | `notebook_id`, `display_name` |
| `create_section` | 在笔记本根级别创建章节 | `notebook_id`, `display_name` |
| `delete_page` | 删除页面(无法恢复) | `page_id`, `dry_run` |
| `move_page_to_section` | 移动页面(克隆 + 删除原件) | `page_id`, `target_section_id`, `dry_run` |
| `clone_page_to_section` | 克隆/移动页面(个人帐户) | `page_id`, `target_section_id`, `delete_source`, `dry_run` |
| `rename_page` | 重命名页面 | `page_id`, `new_title`, `dry_run` |
| `bulk_plan_reorganization` | 生成重组计划 | `notebook_id`, `strategy` |
| `apply_reorganization_plan` | 执行重组计划(批量) | `plan`, `dry_run`, `batch_size`, `offset` |
> **PARA 章节分组**:使用 `create_section_group` 创建顶级文件夹(项目、领域、资源、存档),然后使用 `create_section_in_group` 在每个文件夹中添加章节,然后使用 `move_page_to_section` 将页面移动到这些章节。
> **移动行为**:`move_page_to_section` 和 `clone_page_to_section` (`delete_source=True`)执行真正的移动:克隆页面到目标章节,然后删除原始页面。设置 `delete_source=False` 在 `clone_page_to_section` 上以保留原始页面(复制行为)。
### 移动安全性保证
移动操作强制执行严格的验证,然后再删除原始文件:
1. **空白页面检查** - 没有可见文本内容的页面被跳过(不克隆或删除)
2. **图像下载** - 所有嵌入式图像作为 multipart/form-data 下载并包含在克隆中
3. **克隆验证** - 在将 HTML 发布到目标章节后,服务器验证返回了新的页面 ID
4. **存在检查** - 通过 `get_page_metadata` 读取新页面以确认其可访问
5. **删除仅在验证后** - 仅当所有检查通过时,才删除原始文件
6. **顺序保留** - 页面按逆序处理以按原始顺序出现在目标中
7. **优雅失败** - 如果删除失败,则操作仍报告成功(页面存在于目标中)
## 使用 PARA 方法组织样本提示
[PARA 方法](https://fortelabs.com/blog/para/) 将信息组织成四个类别: **项目**,**领域**,**资源** 和 **存档**。以下是与 Claude Desktop 连接后可使用的提示:
### 发现和评估
```
列出我的所有 OneNote 笔记本和章节。我想在重新组织之前了解我的当前组织结构。
```
```
显示我“快速笔记”章节中的所有页面。我想看看那里累积了什么需要分类。
```
### 计划 PARA 重组
```
我想使用 PARA 方法重组我的“个人”笔记本。请查看我现有的所有章节和页面,然后提出一个计划,创建以下章节:
- 项目(有截止日期的活跃工作)
- 领域(持续的责任)
- 资源(参考材料)
- 存档(已完成或不活跃的项目)
根据页面标题和内容,将每个现有页面分配到最合适的 PARA 类别中。在我批准之前,请先进行 dry run。
```
```
查看我的笔记本并使用“by_topic”策略生成重组计划。然后查看建议并告诉我哪些页面应该移动到哪里,然后再进行任何更改。
```
### 执行重组
```
重组计划看起来不错。请执行 - 创建建议的章节并移动页面,如建议的那样。
```
```
实际上,在您应用完整计划之前,请将我的“Q4 预算”和“Sprint 规划”页面移动到“项目”部分。先进行 dry run。
```
### 清理标题
```
查看我的“快速笔记”章节中的所有页面。许多标题都是“无标题”或仅包含日期。建议更好的标题,并在我批准后重命名它们。
```
```
将标题为“misc stuff march”的页面重命名为“March 2024 Meeting Notes”。在我确认之前,请先进行 dry run。
```
### 持续维护
```
检查我的“项目”部分中最近 3 个月内未修改的任何页面。这些可能是已完成的 - 建议将它们移动到“存档”部分。
```
```
我刚刚完成了我的“网站改版”项目。将所有相关的页面从项目移到存档。
```
## 身份验证详细信息
服务器使用微软的 **设备代码流** - 一种安全的 OAuth 2.0 方法,不需要存储密码或机密:
1. 首次使用时,MCP 服务器日志中会出现一个代码
2. 您访问 https://microsoft.com/devicelogin 并输入代码
3. 使用您的 Microsoft 帐户登录并授予 OneNote 权限
4. 服务器缓存加密的刷新令牌以备将来会话使用
**令牌缓存位置:**
- macOS:`~/Library/Application Support/onenote-organizer/token_cache.bin`
- Linux:`~/.local/share/onenote-organizer/token_cache.bin`
- Windows:`%LOCALAPPDATA%\onenote-organizer\token_cache.bin`
要强制重新身份验证,请删除 `token_cache.bin` 文件。
## 开发
```bash
# 使用 dev 依赖项安装
pip install -e ".[dev]"
# 运行测试
pytest -v
# 运行特定测试文件
pytest tests/test_unit/test_list_tools.py -v
```
## 已知限制
- **移动 = 克隆 + 删除**:Microsoft Graph 不支持原生页面移动。服务器克隆页面(读取 HTML + 下载图片,以 multipart 形式发布到目标部分),然后在验证克隆存在后删除原始页面。页面按逆序依次处理,以保留原始顺序。
- **个人账户 `copyToSection` 501 错误**:Graph API 的 `copyToSection` 端点为个人账户(outlook.com、hotmail.com、live.com)返回“OData 功能未实现”。`clone_page_to_section` 工具解决了这个问题。
- **克隆页面中的图片**:嵌入的图片从源页面下载并作为 multipart form-data 重新上传。如果个别图片下载失败,页面仍然会被克隆,但该图片将显示为损坏的引用。
- **页面排序**:页面按逆序(从后到前)处理,因为 OneNote 将每个新页面放在顶部。处理是顺序的(而非并发的),以保证确定性顺序。
- **无实时同步**:服务器每次调用工具时总是从 Graph 获取最新数据。没有本地缓存或推送通知支持。
- **大型计划**:对于包含数百个页面的笔记本,请使用 `apply_reorganization_plan` 并带有 `batch_size` 和 `offset` 参数,以增量方式处理移动(默认:每批 10 个,顺序处理以保留顺序)。
- **仅设备代码流**:服务器当前支持设备代码身份验证。其他流(客户端凭证、授权代码)可以通过替换 `AuthProvider` 实现。
## 下一步
### 运行可选测试套件
实现包含 21 个可选测试任务(基于属性的测试,带有 `hypothesis` 和额外的单元测试)。编写和运行它们:
```bash
pip install -e ".[dev]"
# 可选测试涵盖:
# - Token 加密往返(属性 1)
# - 分页完整性(属性 2)
# - 响应形状不变性(属性 3)
# - Graph 错误映射(属性 4)
# - 输入验证(属性 5)
# - HTML 转文本剥离(属性 6)
# - 干运行不变性(属性 7)
# - 摘要格式(属性 8、9)
# - 日期范围分组(属性 10)
# - 计划模式有效性(属性 11)
# - 应用摘要计数(属性 12)
# - 部分失败处理(属性 13、14)
# - 日志条目格式(属性 15)
```
### 部署到 AWS
要通过 HTTP/SSE 使服务器可远程访问:
1. **EC2 / ECS 方法**:
```bash
# 在您的服务器上
python -m onenote_organizer --transport http --port 8080
```
将其置于具有 HTTPS 终止的 ALB 或 API Gateway 后面。
2. **Lambda + API Gateway**(高级):
- 将服务器打包为 Lambda 函数
- 使用带有 WebSocket 支持的 API Gateway 进行 SSE
- 在 AWS Secrets Manager 中存储令牌,而不是本地文件
### 添加更多身份验证流
`AuthProvider` 协议使得添加替代身份验证变得容易:
```python
class ClientCredentialAuthProvider:
"""适用于服务器到服务器场景(无需用户交互)。"""
async def get_access_token(self) -> str:
# 使用 MSAL ConfidentialClientApplication
...
```
在 `server.py` 的 `_get_graph_client()` 函数中替换提供程序。
### 发布为包
```bash
# 构建包
python -m build
# 发布到 PyPI
twine upload dist/*
# 用户可以直接安装:
# pip install onenote-organizer
```
## 许可证
MIT
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器