使用说明
备注MCP服务器
在macOS上的Apple notes中创建和管理笔记的综合系统。该项目提供以下主要组成部分:
- MCP服务器 -用于通过stdio/JSON-RPC创建笔记的直接MCP(模型上下文协议)服务器
- 拉工人 -处理GitHub Gist队列中的笔记创建作业的后台服务
- 尾秤入口API -用于创建注释而不复制/粘贴的HTTP API端点(仅限Tailenet)
- 注释导出 -用于将Apple Notes元数据/内容导出到SQLite或JSONL的本地工具
所有组件共享相同的安全控制:身份验证、速率限制、文件夹分配、确认模式和审计日志记录。
先决条件
- macOS (Apple Notes集成所需)
- Python 3.11或更高版本
- Apple Notes应用程序 已安装并配置
- 自动化权限 授予Terminal/Python用于控制Notes(请参阅权限部分)
在macOS上快速安装
在Mac上,您可以使用一个命令安装worker、Python env和Tailscale ingress。秘密存储在 macOS钥匙扣 (无纯文本文件)。
一个命令(安装到 ~/notes_mcp):
curl -fsSL https://raw.githubusercontent.com/apj72/notes_mcp/main/scripts/install_macos.sh | zsh
或者在克隆后在本地运行:
zsh scripts/install_macos.sh
期待什么:
- 飞行前: macOS和Xcode命令行工具已选中。如果缺少CLI工具,系统将提示您安装它们;之后重新运行安装程序。
- 先决条件: 如果需要,可以安装Homebrew,然后
git,python@3.11,以及(除非跳过)尾秤。 - 代表: 如果使用curl一行,脚本会将repo克隆到
~/notes_mcp并从那里继续。 - 秘密: 系统将提示您一次:
- GitHub PAT (个人访问令牌
gist范围) - Gist ID (您的队列Gist)
- 生成MCP令牌并将其存储在Keychain中;安装程序会打印一次(为您的MCP客户端存储)。
- GitHub PAT (个人访问令牌
- 尾秤: 如果Tailscale未登录,安装程序将告诉您打开Tailscale应用程序并登录,然后使用重新运行
--skip-brew(或完成尾秤设置并再次运行)。 - 服务: worker和ingress launchd代理已安装并启动。
选项:
--install-dir <path>--安装到其他目录(默认:~/notes_mcp).--force--覆盖钥匙串条目并重新安装。--skip-tailscale--仅安装工人;跳过尾鳞片和入口。--skip-brew--假设Homebrew和依赖项已经安装。--non-interactive--如果缺少所需的机密,则失败(无提示)。
验证:
# Worker status
launchctl print gui/$(id -u)/com.notes-mcp.worker
# Logs
tail -f /tmp/notes-mcp-worker.out
tail -f /tmp/notes-mcp-worker.err
# Health (local)
curl http://127.0.0.1:8443/health
# Health (tailnet; use your machine’s Tailscale name)
curl http://YOUR_TAILSCALE_NAME:8443/health
# Retrieve MCP token from Keychain
security find-generic-password -s notes_mcp_token -w
卸载:
zsh scripts/uninstall_macos.sh
这将卸载launchd服务,并可选择删除Keychain条目(在删除机密之前会询问您)。
安装
- 克隆或下载此存储库:
cd /path/to/notes_mcp
- 创建虚拟环境(推荐):
python3 -m venv venv
source venv/bin/activate
- 安装依赖项:
python3 -m pip install -e .
备注:在macOS上,使用 python3 -m pip 而不是仅仅 pip 如果 pip 找不到命令。
配置
初始设置
-
复制配置模板:
cp start_worker.sh.example start_worker.sh -
编辑
start_worker.sh并将所有占位符值替换为实际的令牌和ID。 -
生成安全令牌:
# MCP Authentication Token openssl rand -hex 32 # Ingress API Key (for Tailscale Funnel) openssl rand -hex 32
重要提示: start_worker.sh 被忽视并包含敏感秘密。永远不要将其提交给版本控制。
所需的环境变量
NOTES_MCP_TOKEN:用于身份验证的共享密钥令牌GITHUB_TOKEN:GitHub个人访问令牌(对于Gist队列,需要“Gist”范围)NOTES_QUEUE_GIST_ID:您的GitHub Gist ID(来自Gist URL)
可选环境变量
NOTES_MCP_ALLOWED_FOLDERS:逗号分隔的允许文件夹名称列表(默认:“MCP收件箱”)NOTES_MCP_INGRESS_KEY:Tailscale漏斗公共访问的入口API密钥NOTES_MCP_REQUIRE_CONFIRM:如果"true",所有创建请求都必须包括confirm: trueNOTES_QUEUE_POLL_SECONDS:轮询间隔(秒)(默认值:60)NOTES_MCP_MAX_JOB_AGE_SECONDS:作业的最大年龄(秒)(默认值:86400=24小时)
快速开始
选项1:直接MCP服务器(用于游标/IDE集成)
服务器使用stdio传输(标准输入/输出)进行MCP通信。直接运行它:
python3 -m notes_mcp.server
备注:在macOS上,使用 python3 而不是 python 如果 python 找不到命令。
服务器将从stdin读取JSON-RPC请求,并将响应写入stdout。
选项2:Pull Worker(用于通过Gist队列进行异步处理)
设置环境变量(请参阅下面的Pull Worker部分)并运行:
python3 -m notes_mcp.pull_worker
或者设置为后台服务:
./setup_service.sh
选项3:尾标入口API(用于HTTP访问)
启动入口服务:
./start_ingress.sh
然后通过Tailscale曝光:
# For Tailnet-only access (private)
sudo /Applications/Tailscale.app/Contents/MacOS/tailscale serve --bg --http=8443 http://127.0.0.1:8443
# For public access via Funnel (requires ingress key)
sudo /Applications/Tailscale.app/Contents/MacOS/tailscale funnel --bg http://127.0.0.1:8443
注: 对于公共漏斗通道,请确保 NOTES_MCP_INGRESS_KEY 设定在 start_worker.sh 并将其纳入请求中 X-Notes-MCP-Key 头球
光标MCP客户端配置
要将此服务器与Cursor一起使用,请将以下内容添加到Cursor MCP配置文件中(通常 ~/.cursor/mcp.json 或类似):
{
"mcpServers": {
"notes-mcp": {
"command": "python3",
"args": ["-m", "notes_mcp.server"],
"env": {
"NOTES_MCP_TOKEN": "your-secret-token-here",
"NOTES_MCP_ALLOWED_FOLDERS": "MCP Inbox,Work,Personal",
"NOTES_MCP_REQUIRE_CONFIRM": "false"
}
}
}
}
重要:确保 NOTES_MCP_TOKEN 在Cursor配置中匹配您在环境中设置的令牌,或通过 _token 工具调用中的字段。
替代方案:使用包装脚本
如果您更喜欢从文件加载环境变量,请创建一个包装器脚本:
#!/bin/bash
# ~/bin/notes-mcp-server.sh
source ~/.notes-mcp-env
exec python -m notes_mcp.server
然后在Cursor配置中引用它:
{
"mcpServers": {
"notes-mcp": {
"command": "/Users/yourusername/bin/notes-mcp-server.sh"
}
}
}
备注:在包装器脚本中,使用 python3 而不是 python 如果需要的话。
macOS权限
服务器需要“自动化”权限才能控制Apple Notes。当您首次运行服务器时,macOS会提示您授予这些权限。
授予权限
-
自动提示:当服务器首次尝试创建注释时,macOS将显示一个权限对话框。单击“确定”授予访问权限。
-
手动设置:如果需要手动授予权限:
- 打开 系统设置 (或旧版macOS上的系统首选项)
- 首选 隐私和安全 → 自动化
- 找到 终端 (或 python)在列表中
- 启用 备注 在允许的应用程序列表中
-
故障排除:如果权限被拒绝:
- 检查系统设置→ 隐私和安全→ 自动化
- 如果需要,删除并重新添加权限
- 授予权限后重新启动终端/Python
API 参考
工具: notes.create
在Apple Notes中创建新笔记。
参数:
title(字符串,必填):注释标题(最多200个字符)body(字符串,必填):注释正文(最多50000个字符)folder(字符串,可选):目标文件夹名称(默认:“MCP收件箱”)account(字符串,可选):目标帐户-"iCloud"或"On My Mac"(默认值:“icloud”)confirm(布尔值,可选):确认标志(如果是必需的NOTES_MCP_REQUIRE_CONFIRM=true)_token(字符串, 必需的):身份验证令牌(必须匹配NOTES_MCP_TOKEN环境变量)
退货:
{
"ok": true,
"location": {
"account": "iCloud",
"folder": "MCP Inbox"
},
"reference": "timestamp - title"
}
错误:
-32000:验证错误(无效输入、速率限制、拒绝权限等)-32601:未知的工具或方法-32700:分析错误
手动测试
使用Python
创建测试脚本 test_notes_mcp.py:
import json
import subprocess
import os
# Set token
os.environ["NOTES_MCP_TOKEN"] = "test-token-123"
os.environ["NOTES_MCP_ALLOWED_FOLDERS"] = "MCP Inbox"
# Initialize request
init_request = {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {}
}
# Create note request
create_request = {
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "notes.create",
"arguments": {
"title": "Test Note",
"body": "This is a test note created via MCP",
"folder": "MCP Inbox",
"account": "iCloud",
"_token": "test-token-123"
}
}
}
# Run server
proc = subprocess.Popen(
["python3", "-m", "notes_mcp.server"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True
)
# Send requests
proc.stdin.write(json.dumps(init_request) + "\n")
proc.stdin.write(json.dumps(create_request) + "\n")
proc.stdin.close()
# Read responses
for line in proc.stdout:
print(json.loads(line))
运行它:
python test_notes_mcp.py
使用回声和管道
export NOTES_MCP_TOKEN="test-token-123"
export NOTES_MCP_ALLOWED_FOLDERS="MCP Inbox"
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | python3 -m notes_mcp.server
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"notes.create","arguments":{"title":"Test","body":"Test body","_token":"test-token-123"}}}' | python3 -m notes_mcp.server
Pull Worker(基于队列的处理)
pull worker允许您从远程GitHub Gist队列处理笔记创建作业。作业使用HMAC签名以防止篡改,并且worker使用SQLite维护幂等性以防止重复执行。
概述
- 队列:私有GitHub Gist
queue.jsonl(换行符分隔的JSON作业) - 结果:与Gist相同
results.jsonl(附加结果) - 安全:所有作业上的HMAC-SHA256签名
- 幂等性:SQLite数据库跟踪已处理的作业ID
- 重复使用:所有现有的安全控制(满列表、确认、速率限制、日志记录)
作业架构
作业行(queue.json):
{
"job_id": "uuid",
"created_at": "ISO-8601",
"tool": "notes.create",
"args": {
"title": "...",
"body": "...",
"folder": "MCP Inbox",
"account": "iCloud",
"confirm": true
},
"sig": "base64(hmac_sha256(canonical_json))"
}
结果行(results.jsonl):
{
"job_id": "uuid",
"processed_at": "ISO-8601",
"status": "ok|denied|error",
"location": {
"account": "iCloud",
"folder": "MCP Inbox"
},
"reference": "timestamp - title",
"error": {
"code": "...",
"message": "..."
}
}
设置
1.创建私有GitHub Gist
- 首选https://gist.github.com
- 创建新 私有的 要点
- 添加两个文件:
queue.jsonl(最初为空或带有注释)results.jsonl(最初为空)
- 从URL复制Gist ID(例如。,
https://gist.github.com/username/abc123def456→ ID isabc123def456)
2.创建GitHub细粒度代币
- 转到GitHub设置→ 开发者设置→ 个人访问令牌→ 细粒度代币
- 使用以下命令创建新令牌:
- 存储库访问:仅选择注册表
- 权限:
gists(读写) - 过期:根据需要设置
- 复制令牌
3.配置环境变量
编辑 start_worker.sh (创建于 start_worker.sh.example)并设置:
NOTES_QUEUE_GIST_ID-您的GitHub Gist IDGITHUB_TOKEN-您的GitHub个人访问令牌NOTES_MCP_TOKEN-您的MCP身份验证令牌NOTES_MCP_ALLOWED_FOLDERS-逗号分隔的允许文件夹列表NOTES_MCP_INGRESS_KEY-(可选)尾秤漏斗的入口API键
重要:
start_worker.sh被忽视并包含敏感秘密。永远不要承诺。- HMAC密钥默认为
NOTES_MCP_TOKEN如果NOTES_QUEUE_HMAC_SECRET未设置。
4.生成签名作业
使用辅助脚本生成签名的作业行:
python3 -m notes_mcp.sign_job \
--title "My Note Title" \
--body "Note content here" \
--folder "MCP Inbox" \
--account "iCloud" \
--confirm
这将输出一个JSON行,您可以粘贴到其中 queue.jsonl 在你的Gist。
5.运行Worker
前景(用于测试):
python3 -m notes_mcp.pull_worker
launchd(macOS)的背景:
# Automated setup
./setup_service.sh
# Or manually
cp com.notes-mcp.worker.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.notes-mcp.worker.plist
管理服务:
- 开始:
launchctl kickstart -k gui/$(id -u)/com.notes-mcp.worker - 停止:
launchctl bootout gui/$(id -u)/com.notes-mcp.worker - 检查状态:
launchctl list | grep notes-mcp - 查看日志:检查
~/Library/Logs/notes-mcp/目录
安全说明
-
HMAC签名:作业在规范的JSON表示上使用HMAC-SHA256签名。签名涵盖了除以下字段外的所有字段
sig本身。这可以防止任何具有gist访问权限的人注入恶意作业。 -
规范化:工作被以下机构规范化:
- 按字母顺序排序密钥
- 移除
sig领域 - 使用紧凑的JSON(无空格)
-
最小特权:
- GitHub令牌应仅具有gists读/写权限
- 工人只阅读
queue.jsonl并附加到results.jsonl(从不删除或修改现有行) - 所有现有的安全控制(满负荷、确认、速率限制)均适用
-
幂等性:SQLite数据库位于
~/.notes-mcp-queue/worker.sqlite3跟踪已处理的作业ID以防止重放攻击。 -
风险:
- 如果您的GitHub令牌被泄露,攻击者可以读取/写入要点,但如果没有HMAC密钥,则无法创建有效作业
- 如果您的HMAC密钥被泄露,攻击者可以创建有效的作业(但仍受分配列表、确认、速率限制)
- 确保两个秘密的安全
故障排除
GitHub 401/403错误:
- 验证
GITHUB_TOKEN已设置且有效 - 检查令牌是否具有gists读/写权限
- 验证令牌未过期
Gist文件丢失:
- 确保
queue.jsonl和results.jsonl存在于要点中 - 检查
NOTES_QUEUE_GIST_ID是正确的
无效签名:
- 验证
NOTES_QUEUE_HMAC_SECRET(或NOTES_MCP_TOKEN)匹配用于签署作业的内容 - 签名后检查作业未被修改
- 确保规范化匹配(使用
sign_job.py助手)
已处理的作业:
- 这是意料之中的——作业只处理一次
- 检查SQLite数据库:
sqlite3 ~/.notes-mcp-queue/worker.sqlite3 "SELECT * FROM processed_jobs;"
备注自动化权限:
- 与MCP服务器相同-在系统设置中授予→ 隐私和安全→ 自动化
安全功能
- 认证:所有请求都需要一个有效的令牌,通过
NOTES_MCP_TOKEN - 速率限制:每个令牌每分钟最多创建10个呼叫
- 文件夹允许列表:只能使用指定的文件夹(通过配置
NOTES_MCP_ALLOWED_FOLDERS) - 确认模式:写入操作明确确认的可选要求
- 输入验证:标题最多200个字符,正文最多50000个字符,无空字节
- AppleScript注入防御:用户输入作为参数传递,从不连接到脚本字符串中
- 审计日志:记录到的所有操作
~/Library/Logs/notes-mcp/notes-mcp.log
审计日志
所有操作都记录到 ~/Library/Logs/notes-mcp/notes-mcp.log JSON格式。每个日志条目包括:
timestamp:UTC时间戳action:执行的操作(例如“创建”)title_length:注释标题的长度body_length:钞票主体长度account:目标帐户folder:目标文件夹outcome:“允许”、“拒绝”或“错误”error:错误消息(如果结果为“错误”或“拒绝”)
备注:出于隐私考虑,日志不包括全文内容。
故障排除
常见问题
-
“AppleScript错误:Notes出错:无法获取帐户”
- 确保已安装并配置Notes应用程序
- 检查您是否在Notes中设置了iCloud帐户或“在我的Mac上”帐户
- 授予自动化权限(请参阅“权限”部分)
-
“身份验证令牌无效或丢失”
- 确保
NOTES_MCP_TOKEN环境变量已设置 - 验证服务器和客户端之间的令牌匹配
- 确保
-
文件夹“X”不在列表中
- 将文件夹添加到
NOTES_MCP_ALLOWED_FOLDERS环境变量 - 或使用始终允许的“MCP收件箱”
- 将文件夹添加到
-
“需要确认,但未提供”
- 集
confirm: true在工具调用参数中 - 或设置
NOTES_MCP_REQUIRE_CONFIRM="false"禁用确认模式
- 集
-
“超出费率限制”
- 等待60秒后再发出另一个请求
- 每个令牌每分钟最多10次呼叫
-
权限不足
- 检查系统设置→ 隐私和安全→ 自动化
- 确保终端/Python有权控制Notes
- 授予权限后重新启动终端
拉工人问题
-
GitHub 401/403错误
- 验证
GITHUB_TOKEN已设置且有效 - 检查令牌是否具有gists读/写权限
- 验证令牌未过期
- 请求频率超限:
- 等待5-10分钟,然后重试
- 检查您的令牌的速率限制:
curl -H "Authorization: token YOUR_TOKEN" https://api.github.com/rate_limit - 增加轮询间隔:
export NOTES_QUEUE_POLL_SECONDS="30"
- 验证
-
Gist文件丢失
- 确保
queue.jsonl和results.jsonl存在于要点中 - 检查
NOTES_QUEUE_GIST_ID是正确的
- 确保
-
无效签名
- 验证
NOTES_QUEUE_HMAC_SECRET(或NOTES_MCP_TOKEN)匹配用于签署作业的内容 - 签名后检查作业未被修改
- 使用
sign_job.py生成正确签名作业的助手
- 验证
-
已处理的作业
- 这是意料之中的——作业只处理一次(幂等性)
- 检查SQLite数据库:
sqlite3 ~/.notes-mcp-queue/worker.sqlite3 "SELECT * FROM processed_jobs;"
查看日志
tail -f ~/Library/Logs/notes-mcp/notes-mcp.log
尾秤入口API
Tailscale Ingress API提供了一个安全的HTTP端点,用于创建注释,而无需复制/粘贴命令。它只能通过您的Tailnet(而不是公共互联网)访问。
特性
- 仅限Tailnet访问 -只能通过Tailscale VPN访问
- HTTP API -创建笔记的简单POST请求
- 无复制/粘贴 -从iOS快捷方式、webhook等直接调用API。
- 可选身份验证 -附加的基于标头的身份验证(超出Tailscale ACL)
- 速率限制 -每个IP每分钟30个请求
- 重复使用所有安全控制 -文件夹分配列表、确认、验证
快速开始
-
安装依赖项:
python3 -m pip install fastapi uvicorn pydantic -
启动入口服务:
./start_ingress.sh -
通过尾秤曝光:
sudo /Applications/Tailscale.app/Contents/MacOS/tailscale serve --bg --http=8443 http://127.0.0.1:8443 -
测试一下:
curl -X POST http://yourmachine.tailnet.ts.net:8443/notes \ -H "Content-Type: application/json" \ -d '{"title": "Test", "body": "Test content", "folder": "MCP Inbox"}'
API终点:
GET /health-健康检查POST /notes-创建注释(需要X-Notes-MCP-Key标题ifNOTES_MCP_INGRESS_KEY已设置)
请求格式:
{
"title": "Note Title",
"body": "Note content",
"folder": "MCP Inbox",
"account": "iCloud"
}
注释导出
导出工具允许您在本地读取和导出Apple Notes内容,以用于搜索、摘要或备份目的。
特性
- 仅限本地 -所有数据都保留在Mac上
- 隐私第一 -默认情况下仅导出元数据(除非明确要求,否则不包括正文)
- 多种格式 -导出到SQLite或JSONL
- 过滤 -按日期、限制计数、包含/排除正文进行筛选
- 标签提取 -自动从标题前缀中提取标签(例如。,
[WORK]) - 所有文件夹 -导出所有笔记,不考虑文件夹分配列表
- 重复检测 -根据标题和正文的相似性识别重复的注释
用法
# Export metadata only from allowed folders (default)
python3 -m notes_mcp.export_notes
# Export ALL notes from ALL folders (for organization/cleanup)
python3 -m notes_mcp.export_notes --all-folders --max-notes 0 --include-body
# Export with duplicate detection
python3 -m notes_mcp.export_notes --all-folders --find-duplicates --include-body
# Export to SQLite for analysis
python3 -m notes_mcp.export_notes --all-folders --format sqlite --include-body --find-duplicates
整理和清洁注意事项
要组织所有笔记并查找重复项,请执行以下操作:
# Export all notes with bodies and duplicate detection
python3 -m notes_mcp.export_notes --all-folders --max-notes 0 --include-body --find-duplicates --format sqlite
# Then query the SQLite database
sqlite3 .data/notes_export.db "SELECT * FROM notes_export WHERE is_duplicate = 1 ORDER BY duplicate_group, modified_at"
输出文件保存到 .data/ 目录(gitignored)。
发展
容器部署
注意MCP可以在Podman或Docker容器中使用混合架构运行:
- 容器:运行ingress API和/或pull worker(处理HTTP、GitHub Gist、队列管理)
- 主机网桥服务:在macOS主机上运行(通过AppleScript处理Apple Notes)
有关部署,请参阅 部署.md 对于此仓库中的最小工件。
项目结构
notes_mcp/
├── pyproject.toml # Project configuration
├── README.md # This file
├── DEPLOYMENT.md # What's needed to deploy (minimum artifacts)
├── start_worker.sh.example # Configuration template (copy to start_worker.sh)
├── start_ingress.sh.example # Ingress template (copy to start_ingress.sh)
├── setup_service.sh # Worker service setup (launchd)
├── gpt_action.json.example # Custom GPT schema (copy to gpt_action.json)
├── openapi-schema.json # OpenAPI schema
├── CUSTOM_GPT_CONFIG.md # Custom GPT setup guide
├── NOTE_TAGS.md # Tags (hashtags) feature
├── APPLE_NOTES_FORMATTING_SUPPORT.md # HTML formatting in Notes
├── docs/
│ └── launchd/ # Launchd plists (ingress, export)
└── src/
└── notes_mcp/
├── __init__.py
├── server.py # MCP server (stdio)
├── pull_worker.py # Queue-based worker
├── ingress.py # Tailscale ingress API
├── applescript.py # AppleScript integration
├── bridge_client.py # Bridge client (container)
├── bridge_server.py # Bridge server (host)
├── export_notes.py # Notes export tool
├── security.py
├── logging.py
├── formatting.py
├── sign_job.py
└── enqueue_job.py
带有机密的配置文件(start_worker.sh, start_ingress.sh, gpt_action.json,plists)是合法的。辅助脚本、容器文件和实验文档都存在 _unsynced/ (忽略了)。
当前状态
✅ 已实施:
- 带stdio传输的MCP服务器
- 使用GitHub Gist队列拉取worker
- Tailscale Ingress API(HTTP端点)
- Notes导出工具(本地读取权限)
- 全面的安全控制
- 已启动worker和ingress的服务设置
- 队列清除和幂等性
- 文本格式规范化
- 费率限制处理改进
📚 文档:
- 所有组件的完整设置指南
- API参考文件
- 安全审查
- 故障排除指南
🔒 安全:
- HMAC签名作业
- 文件夹分配
- 速率限制
- 可选确认模式
- 审计日志记录(无秘密)
- 输入验证
- AppleScript注入防御
许可证
本项目按原样提供,供当地使用。