mcpField
K
keynote-harvest-mcp

keynote-harvest-mcp

本地服务
github.com/hulin42生产力08 周前更新

关于 keynote-harvest-mcp

keynote-harvest-mcp 是本地 stdio MCP 服务,可从 Keynote 导出文件和 PDF 提取预览、文本、嵌入图像及带版本的清单,适用于需要结构化幻灯片资料的 Agent;不使用 OCR 或 LLM。

主要功能

  1. 01在 macOS 上导出 Keynote 为 PDF
  2. 02生成 PDF 幻灯片预览并提取文本
  3. 03提取嵌入图像资源
  4. 04生成并验证带版本的清单
  5. 05通过 MCP 读取清单和产物

适用场景

  1. 01为幻灯片创建预览图
  2. 02提取幻灯片文本和嵌入图像
  3. 03用清单整理采集的演示文稿产物
  4. 04通过 MCP 检查本地采集结果

MCP 相关

提供本地采集与导出工具,以及清单、预览、提取图像和文本的 MCP 资源。默认不公开源 PDF。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"hulin42-keynote-harvest": {
"command": "npx",
"args": [
"-y",
"keynote-harvest-mcp"
],
"env": {
"KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS": "<KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS>",
"KEYNOTE_HARVEST_ROOT": "<KEYNOTE_HARVEST_ROOT>",
"KEYNOTE_HARVEST_WORKING_DIRECTORY": "<KEYNOTE_HARVEST_WORKING_DIRECTORY>"
}
}
}
}
启动命令npx -y keynote-harvest-mcp
需要配置的环境变量
KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS密钥Path-delimited directories the server may read decks from (e.g. your Keynote documents and Downloads). Required to harvest local files.
KEYNOTE_HARVEST_ROOT密钥Directory where harvests are written. Defaults to .harvests under the working directory.
KEYNOTE_HARVEST_WORKING_DIRECTORY密钥Directory relative paths resolve from. Defaults to the host's current directory.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmkeynote-harvest-mcp0.1.1

工具

5

收录的工具列表 · 更新于 3 周前

参数类型必填说明
redactionModeenum否display | local-debug
searchApplicationsDirboolean否
includeMissingDefaultsboolean否

详细介绍

Keynote Harvest MCP

keynote-harvest-mcp 是一个本地 stdio MCP 服务,可将 Keynote 导出文件和 PDF 转换为可移植的幻灯片预览、可提取文本、内嵌图像资源和带版本号的清单。

它仅包含以收集为先的 MCP 边界:

  • Keynote 应用发现。
  • 在 macOS 上将原生 .key 导出为 PDF。
  • 以 PDF 为先,收集预览、可提取文本、资源和清单。
  • 以适合展示的方式读取清单。
  • 列出本地收集输出。
  • 提供用于清单、预览、提取出的内嵌图像资源和提取文本的 MCP 资源。默认禁止访问源 PDF;只有具备仅供操作者使用的 KEYNOTE_HARVEST_ALLOW_SOURCE_RESOURCES=1 能力时才允许访问。

它有意不包含内容创作/提案工具、Web 框架渲染器、预览路由、OCR、视觉功能和大模型调用。

要求

  • macOS 或 Linux(通过包的 os 字段声明;不支持 Windows)。
  • Node.js 22 或更高版本(受维护的 LTS 版本)。
  • 用于收集 PDF 的 Poppler 命令 pdfinfo、pdftoppm 和 pdftotext。
  • 用于提取内嵌图像资源的 Poppler pdfimages,默认启用。没有 pdfimages 时,收集仍会完成,并在清单中记录警告;传入 extractImages: false(CLI:--no-extract-images)可跳过提取,传入 extractImages: true(CLI:--extract-images)则要求必须有 pdfimages,缺少时会失败。
  • 仅在原生 .key 导出时需要 macOS、Keynote 和自动化权限。

收集 PDF 时无需 Keynote。OCR、视觉功能、大模型调用、.key 包解析和演讲者备注提取均有意不提供。

清单契约

新收集的内容使用 schemaVersion: "keynote-harvest-manifest-v1"。此包维护该契约的三种一致表示:

  • src/types/ 下的 TypeScript 类型。
  • src/schema/ 下的运行时结构验证。
  • 可分发的 Draft 2020-12 JSON Schema:schema/keynote-harvest-manifest-v1.schema.json。

运行时验证器默认要求提供版本号。其显式 allowLegacyVersion 选项仅用于检查在架构版本化之前创建的旧私有清单;新生成的清单始终带有版本号。

架构演进

可以添加可选字段,而无需更改 keynote-harvest-manifest-v1。任何破坏性契约变更都必须创建 keynote-harvest-manifest-v2,并提供单独的架构文件,同时运行时 验证器仍须保留对 v1 的读取支持。allowLegacyVersion 仅限用于 版本化之前的私有清单,不能替代带版本号的架构迁移。

安装、构建和测试

cd keynote-harvest-mcp
npm ci
npm run build
npm test

npm run test:clean-install 会将此包复制到临时目录,运行 npm ci、重新构建,并在与此检出的代码库中已安装依赖隔离的环境下执行由包自身提供的测试。

npm run test:packed-runtime 会构建本地 tarball,将其安装到空的使用者项目中,启动已安装的二进制文件,并验证打包后的工具接口。这两种演练都不会发布任何内容。

npm run test:pdf-runtime 会生成可公开的安全 PDF,通过本地 Poppler 命令测试替换和配额失败,验证路径安全的 v1 清单,并删除所有临时输出。

构建后的服务和工具会从 dist/ 执行 JavaScript;它们不会在运行时执行源 TypeScript。

快速入门

此包已发布到 npm。将每个 stdio MCP 主机指向 npx -y keynote-harvest-mcp; 首次运行时会下载,之后的运行会使用缓存。GUI 主机可能无法继承 shell 的 PATH,因此请为其指定 npx 可执行文件的绝对路径(command -v npx)。若要改为从本地检出版本运行,请参阅本节末尾的“本地构建”。

Claude Code
claude mcp add keynote-harvest \
  -e KEYNOTE_HARVEST_WORKING_DIRECTORY=/path/to/your/working-directory \
  -e KEYNOTE_HARVEST_ROOT=.harvests \
  -e KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS=/path/to/your/decks \
  -- npx -y keynote-harvest-mcp

当服务应在当前项目之外也可用时,请使用 --scope user,并通过 claude mcp get keynote-harvest 验证注册情况。

Claude Desktop

通过 Claude Desktop 的开发者设置,将此服务添加到 claude_desktop_config.json,然后完全退出并重新启动应用:

{
  "mcpServers": {
    "keynote-harvest": {
      "command": "/absolute/path/to/npx",
      "args": ["-y", "keynote-harvest-mcp"],
      "env": {
        "KEYNOTE_HARVEST_WORKING_DIRECTORY": "/path/to/your/working-directory",
        "KEYNOTE_HARVEST_ROOT": ".harvests",
        "KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS": "/path/to/your/decks"
      }
    }
  }
}

Claude Desktop 会在大约 4 分钟后停止每次工具调用,因此处理较长的演示文稿时,请使用 runInBackground: true,并轮询 get_harvest_manifest。

Cursor

在项目中创建 .cursor/mcp.json,或创建 ~/.cursor/mcp.json 以进行全局配置:

{
  "mcpServers": {
    "keynote-harvest": {
      "command": "/absolute/path/to/npx",
      "args": ["-y", "keynote-harvest-mcp"],
      "env": {
        "KEYNOTE_HARVEST_WORKING_DIRECTORY": "/path/to/your/working-directory",
        "KEYNOTE_HARVEST_ROOT": ".harvests",
        "KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS": "/path/to/your/decks"
      }
    }
  }
}
Codex CLI

使用 codex mcp add 注册此服务(stdio 传输):

codex mcp add keynote-harvest \
  --env KEYNOTE_HARVEST_WORKING_DIRECTORY=/path/to/your/working-directory \
  --env KEYNOTE_HARVEST_ROOT=.harvests \
  --env KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS=/path/to/your/decks \
  -- npx -y keynote-harvest-mcp

需要了解 Codex 的两种行为:处理和导出工具标注了 destructiveHint: true,因此 Codex 会在运行这些工具前请求批准——除非绕过批准,否则使用 approval: never 的非交互式 codex exec 会话会取消这些工具(提示“user cancelled MCP tool call”)。此外,Codex 只公开 MCP 工具,不公开资源;请通过 get_harvest_manifest 和处理目录读取结果。Codex 默认的单次调用时限为 300 s;处理较长的演示文稿时,可通过 mcp_servers.keynote-harvest.tool_timeout_sec 延长时限,或使用 runInBackground: true 并轮询。

本地构建

要从检出目录运行,而不是使用 npm:

cd /absolute/path/to/keynote-harvest-mcp
npm ci
npm run build

然后,在上述任一配置中,将 /absolute/path/to/node /absolute/path/to/keynote-harvest-mcp/dist/index.js 用作命令,并保留相同的 env。

不支持:ChatGPT

ChatGPT 只能连接远程(HTTP)MCP 服务。此软件包按设计是本地 stdio 服务——不使用网络,而且原生导出需要同一台 Mac 上安装 Keynote——因此 v0.1 不支持 ChatGPT。

此软件包会根据调用方的当前目录解析相对路径。当 MCP 主机应使用其他工作目录时,请设置 KEYNOTE_HARVEST_WORKING_DIRECTORY。本地源文件必须位于工作目录、处理根目录,或以路径分隔的 KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS 条目中。服务返回相对于处理目录的产物路径和资源 URI,而不是绝对路径。

原生导出会返回 exportedPdfHarvestPath,可将其作为 harvestPdfPath 传递给 harvest_keynote_pdf。get_harvest_manifest 接受处理任务的 slug,而不是任意清单文件路径。

工具响应是摘要,而非清单。每个响应都包含 responseKind:写入工具返回 harvest-summary 和 export-summary,get_harvest_manifest 返回 manifest-summary(其中的 source 块是清单中 source 的扁平化投影,包含 sourceId、sourceKind、sourceDisplayName、harvestTool),后台任务返回 job-record / job-status。清单本身(字段名由架构定义)是 keynote-harvest://<slug>/keynote-harvest-manifest.json 资源。

Keynote 应用选择只信任同时具备许可名单中标识符和 Apple 第一方代码签名的应用包——codesign 叶证书的授权机构必须是“Apple Mac OS Application Signing”或“Software Signing”;Developer ID 应用也会链接到 Apple Root CA,因此仅有根证书并不能证明什么,而应用包标识符本身只是声明,任何应用都可以使用。只有在确实要使用未签名构建时,才设置 KEYNOTE_HARVEST_ALLOW_UNSIGNED_KEYNOTE=1。 通过 GUI 启动的 MCP 主机通常会在最小化的 PATH 下启动服务。本软件包对此进行了补偿:工作进程使用服务自身的 Node 二进制文件运行,查找 Poppler 时也会搜索 /opt/homebrew/bin、/usr/local/bin 和 /opt/local/bin。如果 Poppler 位于其他位置,请将 KEYNOTE_HARVEST_POPPLER_PATH 设为其目录。工作进程接收的是最小化环境,而不会继承主机中无关的密钥。即使导出以 Keynote 错误或超时告终,也只有在 pdfinfo 验证新文件后,才会替换之前的 PDF,因此部分导出的恢复需要 Poppler。长时间运行的工具调用会在主机请求时发送 MCP 进度通知(调用中包含 progressToken):采集过程中会逐页更新;Keynote 导出期间则会发送心跳,可通过 KEYNOTE_HARVEST_PROGRESS_HEARTBEAT_MS 调整。进度通知只有在主机会据此重置请求计时器时才有帮助(Claude Code 支持;可在那里设置 MCP_TOOL_TIMEOUT 来提高上限)。对于设有固定上限的主机——Claude Desktop 会在约 4 分钟后停止每次工具调用——请向 harvest_keynote_pdf 或 export_keynote_to_pdf 传入 runInBackground: true:调用会立即返回一个作业记录,工作进程将在 <harvest root>/.jobs/ 下脱离运行;使用相同 slug 调用 get_harvest_manifest 时,作业运行期间会报告 running 状态和页面进度,完成后则会返回采集的清单、导出的结果(exportedPdfHarvestPath),或作业错误。默认情况下,长时间运行的工作进程会在 10 分钟后作为一个进程组被终止(KEYNOTE_HARVEST_COMMAND_TIMEOUT_MS);脱离运行的后台工作进程会在每个步骤之间自行执行相同的总时限检查,超时后记录作业失败。对于输出超限或超时的工作进程,会先等待 5 秒的终止宽限期(KEYNOTE_HARVEST_COMMAND_KILL_GRACE_MS),然后再强制终止。每次 Poppler 调用的时限为 2 分钟(KEYNOTE_HARVEST_POPPLER_TIMEOUT_MS)。

默认情况下,幻灯片预览以 144 DPI 渲染;若预览也要作为可重复使用的图像,请传入 previewDpi(36-600,CLI:--preview-dpi)以提高分辨率。默认情况下,采集最多处理 300 页,并记录截断警告;传入 maxPages(1-2000,CLI:--max-pages)可更改上限。输入大小上限默认为 512 MB(KEYNOTE_HARVEST_MAX_INPUT_BYTES),生成的暂存输出上限为 2 GB(KEYNOTE_HARVEST_MAX_OUTPUT_BYTES),捕获的命令输出上限为 1 MB(KEYNOTE_HARVEST_MAX_COMMAND_OUTPUT_BYTES),资源读取上限为 10 MB(KEYNOTE_HARVEST_MAX_RESOURCE_BYTES)。嵌入图像提取也会通过 KEYNOTE_HARVEST_MAX_EXTRACTED_ASSETS 和 KEYNOTE_HARVEST_MAX_ASSET_PIXELS 限制资源数量和单个资源的像素数。预览渲染受逐页像素预算限制,预算根据每页自身的尺寸计算(KEYNOTE_HARVEST_MAX_PREVIEW_PIXELS,默认值为 40,000,000):超大页面会以较低 DPI 渲染,并在清单中发出警告;即使以 36 DPI 渲染仍超出预算的页面则会被拒绝。嵌入图像提取会遵循 maxPages 的限制。采集和导出的输出会在针对各目标位置的锁定下替换(KEYNOTE_HARVEST_REPLACE_LOCK_TIMEOUT_MS、KEYNOTE_HARVEST_REPLACE_LOCK_STALE_MS);如果替换失败后无法恢复之前的输出,则会保留备份副本,并在错误中包含其路径。

工具参数会使用 zod 按照 tools/list 中公布的相同架构进行验证;无效调用会立即失败,并提供逐字段的错误消息。

安全

此服务以其 MCP 宿主的本地文件系统和应用权限运行。其 stdio 传输没有独立的身份验证边界。仅在可信的宿主中配置它,限制允许的输入根目录,并假设任何提取的文本或图像都可能包含对抗性指令。演示文稿内容仅供检查,绝不能作为调用工具、更改配置或泄露其他文件的依据。

详细控制措施:

  • 提取 slug 仅接受小写字母、数字和内部连字符。
  • 源文件读取仅限于工作目录、提取根目录以及操作员配置的 KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS,并进行词法和符号链接检查。
  • 工具写入范围限制为 <harvest root>/<slug> 和 <harvest root>/<slug>/source。写入其他位置需要同时满足客户端参数和操作员设置的 KEYNOTE_HARVEST_ALLOW_OUTSIDE_ROOT=1。
  • 导出和提取输出先在暂存路径中生成、验证,再以事务方式替换。运行失败时会保留之前的 PDF、清单、预览图、文本、资源和源文件。
  • 资源读取除词法路径和真实路径 containment 检查外,还使用明确的清单/预览图/文本/资源允许列表。除非操作员设置 KEYNOTE_HARVEST_ALLOW_SOURCE_RESOURCES=1,否则禁止读取源 PDF 和导出摘要。
  • JSON 资源会移除已知的本地路径字段,并对警告字符串中匹配的路径进行脱敏。
  • 只有同时设置 redactionMode: "local-debug" 和操作员设置的 KEYNOTE_HARVEST_ALLOW_LOCAL_DEBUG=1,才会显示绝对响应路径和命令诊断信息。客户端无法选择原始模式。
  • Keynote 应用必须位于 /Applications 下,使用允许的 Apple Keynote bundle identifier,并带有 Apple 第一方代码签名。操作员可通过 KEYNOTE_HARVEST_ALLOWED_KEYNOTE_APP_ROOTS 添加以路径分隔的根目录,并通过 KEYNOTE_HARVEST_ALLOWED_KEYNOTE_BUNDLE_IDS 扩展 bundle ID 允许列表。
  • 工具会声明只读/破坏性/幂等/开放世界 MCP 注解。这些是宿主提示,不是授权控制。
  • 此软件包不执行遥测或网络请求。它仅调用本地 Node 运行时、Poppler 工具、osascript 和 Apple Keynote。

请将 .harvests/ 添加到每个宿主项目的 .gitignore;生成的清单和源文件/导出摘要是本地工作产物,不是用于发布的测试夹具。威胁模型和漏洞披露政策请参阅 SECURITY.md。

.github/workflows/ci.yml 中的 CI 工作流会使用 Node 22 和 24 运行软件包测试、干净安装和打包运行时演练,并在 Linux 上使用 Poppler 实际提取 PDF。

此软件包以 keynote-harvest-mcp 的名称发布到 npm,并通过 .github/workflows/release.yml 中的发布工作流提供来源证明;安全报告通过 hulin42/keynote-harvest-mcp 的 GitHub 私密漏洞报告功能提交(请参阅 SECURITY.md)。

传输

此服务使用官方 @modelcontextprotocol/sdk stdio 传输(由 SDK 处理以换行符分隔的 JSON-RPC、协议版本协商和 ping)。烟雾测试和打包运行时演练会使用官方 SDK 客户端连接,因此每次测试运行都会执行符合规范的 MCP 握手。早期的一个内部迭代版本曾使用手工实现的 Content-Length 分帧方式,但主流 MCP 宿主都不支持这种方式;该层现已移除。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。