Evernote API服务器

一个Python MCP服务器,为AI助手提供完整的Evernote账户访问权限,支持笔记的搜索、创建、更新、删除以及标签和笔记本管理。

  • by seanjmartin
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 seanjmartin/evernote-mcp

使用说明

Evernote MCP服务器

Python MCP(模型上下文协议)服务器,使AI助手可以完全访问您的Evernote帐户。13个用于搜索、阅读、创建、更新和删除笔记的工具;管理标签和笔记本;处理附件和加密内容;监控写入操作。

需要Python 3.12以上。


快速开始

# Install
pip install -e .

# Authenticate (one-time, opens browser)
python src/evernote_client.py --auth

# Run the server
python src/evernote_mcp.py

认证

服务器使用OAuth 1.0a访问您的Evernote帐户。您需要进行一次身份验证;令牌持续大约一年。

创建令牌

python src/evernote_client.py --auth

这将:

  1. 打开默认浏览器进入Evernote的授权页面
  2. 在上启动回调服务器 localhost:8080
  3. 授权后,Evernote将重定向回回调
  4. 令牌保存到 .evernote-token.json 在项目根中

如果浏览器流失败(例如,在无头服务器上运行),脚本将提示您粘贴 oauth_verifier 5分钟超时后手动设置值。

令牌文件

令牌存储在 .evernote-token.json:

{
  "access_token": "S=s46:U=...:E=...:H=...",
  "created": 1771265208024,
  "expires": 1802801208024
}
  • access_token -用于所有API调用的OAuth令牌字符串
  • created --获得令牌时的Unix毫秒数
  • expires --令牌过期时的Unix毫秒数(约1年)

令牌过期后,重新运行 python src/evernote_client.py --auth 买一个新的。

验证令牌

python src/evernote_client.py --test

这将测试连接并打印您的用户名、用户ID和笔记本计数。


配置

命令行参数

运输方式 (互斥;默认为stdio):

标志 默认值 描述
--stdio 在(未指定其他传输时) 克劳德代码/MCP客户端集成的stdio传输
--http 或 --sse 关闭 HTTP/SSE服务器
--https 关闭 HTTPS/SSE服务器(需要 --ssl-cert 和 --ssl-key)

HTTP选项:

标志 默认值 描述
--http-port 8082 HTTP服务器端口
--http-host 127.0.0.1 HTTP服务器绑定地址

HTTPS选项:

标志 默认值 描述
--https-port 8443 HTTPS服务器端口
--https-host 127.0.0.1 HTTPS服务器绑定地址
--ssl-cert (必需) SSL证书文件的路径
--ssl-key (必需) SSL私钥文件的路径

令牌和队列:

标志 默认值 描述
--token-file .evernote-token.json OAuth令牌文件的路径
--queue-pace 10 排队写入操作之间的秒数

环境变量

变量 默认值 描述
EVERNOTE_TOKEN_FILE .evernote-token.json 令牌文件的路径(被覆盖 --token-file)
EVERNOTE_QUEUE_PACE_SECONDS 10 写入队列步调(被覆盖 --queue-pace)
EVERNOTE_CONSUMER_KEY mcpserver-6972 OAuth消费者密钥
EVERNOTE_CONSUMER_SECRET (内置) OAuth消费者秘密
EVERNOTE_SANDBOX false 设置为 true 沙盒(注意:Evernote沙盒已退役)

Claude代码集成

将此添加到您的 .mcp.json (或克劳德代码设置):

{
  "mcpServers": {
    "evernote": {
      "command": "python",
      "args": ["/path/to/evernote-mcp/src/evernote_mcp.py"],
      "env": {
        "EVERNOTE_TOKEN_FILE": "/path/to/evernote-mcp/.evernote-token.json"
      }
    }
  }
}

使用示例

# stdio mode (default, for Claude Code)
python src/evernote_mcp.py

# HTTP on a custom port
python src/evernote_mcp.py --http --http-port 9000

# HTTPS with SSL certificates
python src/evernote_mcp.py --https --ssl-cert cert.pem --ssl-key key.pem

# Custom token file and faster queue pacing
python src/evernote_mcp.py --token-file /secure/token.json --queue-pace 5

工具参考

总结

工具 类型 描述
list_notebooks 阅读 列出所有笔记本
list_tags 读取 列出所有具有层次结构的标签
search_notes 阅读 搜索笔记;可选择获取每个笔记本/标签的计数
get_note 阅读 以Markdown格式阅读笔记;版本历史;相关注释
get_attachment 读取 获取资源元数据、数据和OCR文本
get_user_info 阅读 帐户信息、保费状态、上传配额
create_note 写 从Markdown创建笔记或克隆现有笔记
update_note 写入 编辑内容、元数据、标签、附件
delete_note 写 将便条移到垃圾箱(可恢复30天)
manage_tags 写入 创建、重命名或删除标签
add_attachment 写入 将文件附加到现有笔记
manage_notebooks 写/读 创建、更新笔记本;获取默认笔记本
check_queue 队列 监视和管理排队的写入操作

所有写工具都要经过一个有节奏的队列,以避免Evernote的速率限制。如果队列为空,并且已过起搏间隔,则立即执行写入。否则,它们将在后台排队和处理。

所有工具归还 {"success": true, ...} 关于成功或 {"success": false, "error": "...", "error_type": "..."} 失败。


阅读工具

list_notebooks

列出所有Evernote笔记本。

参数: 无

退货: notebooks (列表 {guid, name, stack, defaultNotebook, created, updated}), count


list_tags

列出所有具有父层次结构和完整路径的标签。

参数: 无

退货: tags (列表 {guid, name, parentGuid, parentName, path}), count

这 path 字段显示完整的层次结构,例如。 "Parent/Child/Grandchild".


search_notes

使用Evernote的搜索语法搜索笔记。还可以返回每个笔记本和标签的笔记计数,而不是笔记结果。

参数 类型 默认值 说明
query str "" 搜索查询(请参见 搜索语法)
notebook str "" 此笔记本的范围搜索(名称或GUID)
max_results int 25 可退回的最大钞票数
offset int 0 分页偏移
counts_only bool的。 False 返回每个笔记本/标签的笔记计数,而不是笔记列表

回报(正常): notes (列表 {guid, title, created, updated, notebookGuid, notebookName, tags, ...}), totalNotes, offset, hasMore

退货(仅限counts_only): notebookCounts (数到名字), tagCounts, trashCount


get_note

以Markdown格式阅读笔记内容。还支持版本历史记录(高级)和查找相关注释。

参数 类型 默认值 说明
guid str 必需 注意GUID
include_content bool的。 True 包括注释正文
passphrase str "" 解密 <en-crypt> 与此密码短语内联的块
list_versions bool的。 False 返回版本历史记录而不是内容(高级)
version int 0 通过updateSequenceNum返回特定版本(高级)
find_related bool的。 False 包括相关注释和标签

回报(正常): note 随着 {guid, title, content_markdown, notebookGuid, notebookName, tags, created, updated, attributes, resources}

返回(list_versions): versions (列表 {updateSequenceNum, updated, saved, title})

返回(版本>0): 该特定版本的注释内容

返回值(find_related): 正常的音符响应,带有额外的 related 包含相关注释和标签的字段


get_attachment

获取资源/附件元数据,以及可选的二进制数据或OCR识别文本。

通过任一方式识别资源 resource_guid 或以下各项的组合 note_guid + resource_hash.

参数 类型 默认值 说明
resource_guid str "" 直接资源GUID
note_guid str "" 注释GUID(与 resource_hash)
resource_hash str "" 资源的MD5十六进制哈希
include_data bool的。 False 包含base64编码的文件数据
include_recognition bool的。 False 包括OCR/识别文本

退货: resource 随着 {guid, noteGuid, mime, hash, size, filename, width, height} 加 data_base64 (如有要求)以及 recognition (如有要求)


get_user_info

获取帐户信息、保费状态、上传配额和同步状态。

参数: 无

退货: username, email, name, userId, privilege, accounting (上传限制), premium (状态、到期), syncState (当前时间、更新计数、上传字节数)


写入工具

所有写工具都是通过有节奏的队列提交的。当立即执行时,它们直接返回操作结果。排队后,它们会返回:

{
  "success": true,
  "queued": true,
  "job_id": "abc12345",
  "message": "Queued. Use check_queue to monitor.",
  "queue_position": 1
}

create_note

从Markdown内容创建新笔记,或克隆现有笔记。

参数 类型 默认值 说明
title str 必填 注释标题
content str "" Markdown中的注释正文
notebook str "" 目标笔记本(名称或GUID;如果为空,则使用默认值)
tags str "" 逗号分隔的标记名(如果不存在,则自动创建)
attachment_paths str "" 要附加的逗号分隔的本地文件路径
copy_from_guid str "" 克隆此笔记,而不是从内容创建

当 copy_from_guid 已设置 title, content,以及 tags 参数被忽略。这 notebook 参数可以覆盖目标笔记本。

退货: note 随着 {guid, title, notebookGuid, notebookName, created}


update_note

更新现有笔记的内容、元数据或两者。当仅元数据更改时,自动使用仅限元数据的轻量级路径(1个API调用);当内容或附件更改时,则自动使用完整内容路径(2个API调用)。

参数 类型 默认值 说明
guid str 必需 注意GUID
title str "" 新标题(空=保持最新)
content str "" 新Markdown内容(空=保持最新)
tags str "" 逗号分隔的标记名称,替换所有(空=保持最新)
notebook str "" 移动到此笔记本(空=保持最新)
created str "" 覆盖创建日期(ISO或YYYYMMDD)
reminder_time str "" 设置提醒(ISO或YYYYMMDD)
author str "" 设置作者属性
source_url str "" 设置源URL属性
latitude float 无 设置纬度
longitude float 无 设置经度
attachment_paths str "" 要附加的逗号分隔的本地文件路径

退货: note 随着字段的更新, update_type ("metadata_only" 或 "full_content")


delete_note

把一张纸条移到垃圾桶里。可通过Evernote UI恢复30天。

参数 类型 默认值 说明
guid str 必需 注意GUID

退货: guid, deleted: true


manage_tags

创建、重命名或删除标记。

参数 类型 默认值 说明
action str 必填 "create", "rename",或 "delete"
name str "" 标记名称(用于创建,或查找以重命名/删除)
guid str "" 标记GUID(用于重命名/删除的名称替代)
new_name str "" 新名称(用于重命名)
parent str "" 父标记名称或GUID(用于创建)

add_attachment

将文件附加到现有笔记。提供本地文件路径或具有文件名和MIME类型的base64数据。

参数 类型 默认值 说明
note_guid str 必需 注意要附加的GUID
file_path str "" 本地文件的路径
data_base64 str "" Base64编码文件数据(file_path的替代方案)
filename str "" 文件名(data_base64需要)
mime_type str "" MIME类型(data_base64需要MIME类型,例如。 "image/png")

manage_notebooks

创建或更新笔记本,或获取默认笔记本。

参数 类型 默认值 说明
action str 必填 "create", "update",或 "get_default"
name str "" 笔记本名称
guid str "" 笔记本GUID(用于更新)
new_name str "" 新名称(用于更新)
stack str "" 堆栈名称(用于创建/更新)

这 get_default 动作是一个读取操作,并立即执行(绕过写入队列)。


排队工具

check_queue

监视和管理排队的写入操作。

参数 类型 默认值 说明
job_id str "" 检查特定作业(空=显示全部)
status str "" 筛选器: "pending", "processing", "completed", "failed"
clear_completed bool的。 False 从历史记录中删除已完成/失败的作业

退货: jobs 列表, count, summary (按状态计数), paused (布尔), pause_remaining_seconds, pace_seconds


建筑

MCP Client (Claude Code, etc.)
        |
        v  stdio / HTTP+SSE / HTTPS+SSE
  evernote_mcp.py       -- FastMCP server, 13 tool definitions, transport setup
        |
        +---> write_queue.py       -- Paced write queue, background thread, JSON persistence
        |
        +---> evernote_client.py   -- EvernoteAPI class, OAuth, SDK wrapper, all API methods
        |
        +---> enml_converter.py    -- ENML <-> Markdown conversion, en-crypt decryption

源文件

src/evernote_mcp.py --服务器入口点。定义所有13个 @mcp.tool() 函数、用于多传输启动的argparse以及队列调用的写执行器函数。读取工具直接调用API;编写工具提交到队列。

src/evernote_client.py — EvernoteAPI 类包装Evernote SDK。所有方法都返回纯字典。处理OAuth流、令牌持久性、速率限制检测、RTE(实时编辑)冲突检测、名称/GUID解析缓存以及具有指数回退的瞬态错误重试。

src/enml_converter.py --双向ENML到Markdown转换器。笔记在Evernote中存储为ENML,但MCP服务器接受并返回Markdown。资源被引用为 ![filename](evernote-resource:<hash>)。还处理AES-128-CBC解密 <en-crypt> 阻碍。

src/write_queue.py --所有写入操作的持久定步调队列。默认速度:API调用间隔10秒。如果Evernote返回速率限制,队列将暂停服务器指定的持续时间。队列状态将持久化为 .evernote-queue.json 因此,挂起的作业在服务器重启后仍然有效。

关键模式

写入队列步调 --写入操作的节奏是为了避免Evernote的速率限制。默认写入间隔为10秒(--queue-pace 或 EVERNOTE_QUEUE_PACE_SECONDS).如果队列为空,并且自上次写入以来已经过了足够的时间,则操作将立即执行。否则,它们将被排队并由后台线程处理。

费率限制与RTE冲突 -Evernote的API回报 RATE_LIMIT_REACHED 针对两种不同的情况。真实速率限制使用长持续时间(900-3600秒)。当笔记在Evernote桌面客户端中打开(实时编辑)时,API也会返回 RATE_LIMIT_REACHED 但持续时间短(<300秒)。服务器使用300秒的阈值来区分这些:短持续时间增加 RTEConflictError,长时间上涨 RateLimitError.

元数据与内容更新 — update_note 自动选择最有效的路径。如果只有元数据在更改(标题、标记、笔记本、日期),则使用单个API调用来避免RTE冲突。如果内容或附件正在更改,它将使用双调用路径(get+update)。

ENML转换 --笔记在Evernote中存储为ENML(一种受限的XML格式)。服务器自动转换Markdown。资源(图像、文件)使用以下方式引用 evernote-resource:<md5hash> URL。转换器处理复选框(<en-todo>),加密块(<en-crypt>),并删除不允许使用的HTML标签。


搜索语法

这 search_notes 该工具接受Evernote的搜索语法:

操作员 示例 描述
notebook: notebook:"My Notebook" 在特定笔记本中搜索
tag: tag:important 带有此标签的注释
-tag: -tag:archive 没有此标签的注释
intitle: intitle:meeting 仅在笔记标题中搜索
created: created:20240101 在此日期或之后创建的注释(YYYYMMDD)
updated: updated:20240601 在此日期或之后更新的注释
resource: resource:image/png 包含此MIME类型的注释
todo: todo:true 带有未选中复选框的注释
* meet* 通配符(匹配“会议”、“会议”等)
"..." "exact phrase" 精确短语匹配
any: any: cat dog 匹配任何术语(默认为全部)
- -archive 排除术语

操作员可以组合: notebook:"Work" tag:urgent intitle:project created:20240101


错误处理

所有工具都返回一致的响应格式:

{"success": true, "...": "..."}

出现错误时:

{"success": false, "error": "description", "error_type": "ERROR_CODE"}
错误类型 原因 解决方法
RATE_LIMITED Evernote API配额耗尽 等待 retry_after_seconds (已包含在回复中)
RTE_CONFLICT 笔记已在Evernote桌面客户端中打开 关闭Evernote中的笔记并重试
NOT_FOUND 未找到笔记本或资源 请检查名称/GUID
ERROR 其他错误 查看错误消息以了解详细信息

排队写入失败标记为 "failed" 在队列中。使用 check_queue 查看失败的作业及其错误消息。


发展

设置

pip install -e ".[dev]"

运行测试

# All tests
python -m pytest tests/ -v

# With coverage report
python -m pytest tests/ --cov=src --cov-report=term-missing

# Integration tests (requires valid token)
EVERNOTE_INTEGRATION=1 python -m pytest tests/test_integration.py -v

测试结构

文件 测试 封面
test_enml_converter.py 40 ENML/Markdown转换、解密
test_evernote_client.py 69 API包装器、OAuth、速率限制、所有SDK方法
test_evernote_mcp.py 92 MCP工具功能、执行器、错误处理
test_write_queue.py 29 队列逻辑、持久性、节奏、后台处理
test_integration.py 3 真正的API调用(默认情况下跳过)

230个单元测试,95%以上的代码覆盖率。测试模拟Evernote SDK note_store/user_store 客户端测试级别和模拟 EvernoteAPI MCP工具测试方法。


依赖项

运行时间: evernote3, oauth2, mcp, fastmcp, markdownify, markdown-it-py, beautifulsoup4, lxml, cryptography, uvicorn

发展: pytest, pytest-mock, pytest-cov


许可证

版权所有(c)2026肖恩·马丁。MIT许可证。看 许可证 了解详情。