MCP server that harvests Keynote and PDF slide decks into slide previews, extracted text, embedded-image assets, and a versioned manifest. PDF-first anywhere; native .key export via Keynote on macOS.
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Documented (README)
claude mcp add keynote-harvest-mcp -- npx -y keynote-harvest-mcp{
"mcpServers": {
"keynote-harvest-mcp": {
"command": "npx",
"args": ["-y", "keynote-harvest-mcp"]
}
}
}MCP Servers overview
# Keynote Harvest MCP
`keynote-harvest-mcp` is a local stdio MCP server for turning Keynote exports and PDFs into portable slide previews, extractable text, embedded-image assets, and a versioned manifest.
It contains only the harvest-first MCP boundary:
- Keynote app discovery.
- Native `.key` to PDF export on macOS.
- PDF-first harvest into previews, extractable text, assets, and a manifest.
- Display-safe manifest reads.
- Local harvest output listing.
- MCP resources for manifests, previews, extracted embedded-image assets, and extracted text. Source PDFs are denied by default and require the operator-only `KEYNOTE_HARVEST_ALLOW_SOURCE_RESOURCES=1` capability.
It intentionally excludes composition/proposal tools, web-framework renderers, preview routes, OCR, vision, and LLM calls.
## Requirements
- macOS or Linux (declared via the package `os` field; Windows is unsupported).
- Node.js 22 or newer (maintained LTS releases).
- Poppler commands `pdfinfo`, `pdftoppm`, and `pdftotext` for PDF harvests.
- Poppler `pdfimages` for embedded-image asset extraction, which is on by default. Without `pdfimages` the harvest still completes and records a manifest warning; pass `extractImages: false` (CLI: `--no-extract-images`) to skip extraction, or `extractImages: true` (CLI: `--extract-images`) to require `pdfimages` and fail when it is missing.
- macOS, Keynote, and Automation permission for native `.key` export only.
PDF harvesting can run without Keynote. OCR, vision, LLM calls, `.key` package parsing, and presenter-note extraction are intentionally absent.
## Manifest Contract
New harvests use `schemaVersion: "keynote-harvest-manifest-v1"`. The package owns three aligned representations of that contract:
- TypeScript types under `src/types/`.
- Runtime structural validation under `src/schema/`.
- A distributable Draft 2020-12 JSON Schema at `schema/keynote-harvest-manifest-v1.schema.json`.
The runtime validator requires the version by default. Its explicit `allowLegacyVersion` option exists only to inspect older private manifests created before schema versioning; newly generated manifests are always versioned.
### Schema Evolution
Additive, optional fields may be introduced without changing
`keynote-harvest-manifest-v1`. Any breaking contract change must mint
`keynote-harvest-manifest-v2` with a separate schema file, while the runtime
validator retains support for reading v1. `allowLegacyVersion` remains limited
to pre-versioned private manifests and is not a substitute for versioned schema
migrations.
## Install, Build, And Test
```bash
cd keynote-harvest-mcp
npm ci
npm run build
npm test
```
`npm run test:clean-install` copies the package to a temporary directory, runs `npm ci`, rebuilds it, and executes the package-owned tests in isolation from this checkout's installed dependencies.
`npm run test:packed-runtime` builds a local tarball, installs it into an empty consumer project, starts the installed binary, and verifies the packaged tool surface. Neither rehearsal publishes anything.
`npm run test:pdf-runtime` generates public-safe PDFs, exercises replacement and quota failures through local Poppler commands, validates path-safe v1 manifests, and removes all temporary output.
The built server and tools execute JavaScript from `dist/`; they do not execute source TypeScript at runtime.
## Quickstart
The package is on npm. Point each stdio MCP host at `npx -y keynote-harvest-mcp`;
the first run downloads it, later runs use the cache. GUI hosts may not
inherit your shell's PATH, so give them the absolute `npx` executable
(`command -v npx`). To run from a local checkout instead, see "Local build"
at the end of this section.
### Claude Code
```bash
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
```
Use `--scope user` when the server should be available outside the current
project, and verify the registration with `claude mcp get keynote-harvest`.
### Claude Desktop
Add this server to `claude_desktop_config.json` through Claude Desktop's
developer settings, then fully quit and relaunch the application:
```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 stops every tool call at roughly four minutes, so harvest
long decks with `runInBackground: true` and poll `get_harvest_manifest`.
### Cursor
Create `.cursor/mcp.json` in a project, or `~/.cursor/mcp.json` for a global
configuration:
```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
Register the server with `codex mcp add` (stdio transport):
```bash
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
```
Two Codex behaviors to know: the harvest and export tools are annotated
`destructiveHint: true`, so Codex asks for approval before running them —
non-interactive `codex exec` sessions with `approval: never` cancel them
("user cancelled MCP tool call") unless approvals are bypassed. And Codex
exposes MCP tools only, not resources; read results through
`get_harvest_manifest` and the harvest directory. Codex's default per-call limit is 300 s; raise it
for long decks with `mcp_servers.keynote-harvest.tool_timeout_sec`, or use
`runInBackground: true` and poll.
### Local build
To run from a checkout instead of npm:
```bash
cd /absolute/path/to/keynote-harvest-mcp
npm ci
npm run build
```
Then use `/absolute/path/to/node /absolute/path/to/keynote-harvest-mcp/dist/index.js`
as the command in any of the configurations above, keeping the same `env`.
### Not supported: ChatGPT
ChatGPT connects only to remote (HTTP) MCP servers. This package is a local
stdio server by design — no network, and native export needs Keynote on the
same Mac — so ChatGPT is out of scope for v0.1.
The package resolves relative paths from the caller's current directory. Set `KEYNOTE_HARVEST_WORKING_DIRECTORY` when an MCP host should use a different working directory. Local source files must be inside the working directory, harvest root, or a path-delimited `KEYNOTE_HARVEST_ALLOWED_INPUT_ROOTS` entry. The server returns harvest-relative artifact paths and resource URIs rather than absolute paths.
Native export returns `exportedPdfHarvestPath`, which can be passed to `harvest_keynote_pdf` as `harvestPdfPath`. `get_harvest_manifest` accepts a harvest `slug`, not an arbitrary manifest file path.
Tool responses are summaries, not the manifest. Every response carries a `responseKind`: `harvest-summary` and `export-summary` from the write tools, `manifest-summary` from `get_harvest_manifest` (its `source` block is a flattened projection — `sourceId`, `sourceKind`, `sourceDisplayName`, `harvestTool` — of the manifest's `source`), and `job-record` / `job-status` for background jobs. The manifest itself, with its schema-defined field names, is the `keynote-harvest://<slug>/keynote-harvest-manifest.json` resource.
Keynote app selection trusts only bundles that carry an allowlisted identifier *and* an Apple first-party code signature — the `codesign` leaf authority must be "Apple Mac OS Application Signing" or "Software Signing"; Developer ID apps chain to Apple Root CA too, so the root alone proves nothing, and a bundle identifier alone is a claim any app can make. Set `KEYNOTE_HARVEST_ALLOW_UNSIGNED_KEYNOTE=1` only for deliberately unsigned builds.
GUI-launched MCP hosts often start servers with a minimal PATH. The package compensates: worker processes run under the server's own Node binary, and Poppler lookups also search `/opt/homebrew/bin`, `/usr/local/bin`, and `/opt/local/bin`. If Poppler lives elsewhere, set `KEYNOTE_HARVEST_POPPLER_PATH` to its directory. Workers receive a minimal environment rather than inheriting unrelated host secrets. Exports that finish with a Keynote error or timeout replace a previous PDF only after `pdfinfo` verifies the new file, so partial-export recovery requires Poppler. Long-running tool calls stream MCP progress notifications when the host requests them (a `progressToken` on the call): per-page updates during harvests, and a heartbeat during Keynote exports tunable via `KEYNOTE_HARVEST_PROGRESS_HEARTBEAT_MS`. Progress only helps hosts that reset their request timer on it (Claude Code does; set `MCP_TOOL_TIMEOUT` there to raise its ceiling). Hosts with a fixed cap — Claude Desktop stops every tool call at roughly four minutes — should pass `runInBackground: true` to `harvest_keynote_pdf` or `export_keynote_to_pdf`: the call returns immediately with a job record, the worker runs detached under `<harvest root>/.jobs/`, and `get_harvest_manifest` with the same slug reports `running` with page progress until the result lands, then returns the manifest for harvests or the export result (`exportedPdfHarvestPath`) for exports, or the job's error. Long-running workers are killed as a process group after 10 minutes by default (`KEYNOTE_HARVEST_COMMAND_TIMEOUT_MS`), and detached background workers enforce the same total budget themselves between steps, recording a failed job when it is exceeded; outpuWhat people ask about keynote-harvest-mcp
What is hulin42/keynote-harvest-mcp?
+
hulin42/keynote-harvest-mcp is mcp servers for the Claude AI ecosystem. MCP server that harvests Keynote and PDF slide decks into slide previews, extracted text, embedded-image assets, and a versioned manifest. PDF-first anywhere; native .key export via Keynote on macOS. It has 0 GitHub stars and its last recorded update is dated 2026-09-02.
How do I install keynote-harvest-mcp?
+
You can install keynote-harvest-mcp by cloning the repository (https://github.com/hulin42/keynote-harvest-mcp) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is hulin42/keynote-harvest-mcp safe to use?
+
Our security agent has analyzed hulin42/keynote-harvest-mcp and assigned a Trust Score of 87/100 (tier: Trusted). See the full breakdown of passed checks and flags on this page.
Who maintains hulin42/keynote-harvest-mcp?
+
hulin42/keynote-harvest-mcp is maintained by hulin42. The last recorded GitHub activity is dated 2026-09-02, with 0 open issues.
Are there alternatives to keynote-harvest-mcp?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy keynote-harvest-mcp to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/hulin42-keynote-harvest-mcp)<a href="https://claudewave.com/repo/hulin42-keynote-harvest-mcp"><img src="https://claudewave.com/api/badge/hulin42-keynote-harvest-mcp" alt="Featured on ClaudeWave: hulin42/keynote-harvest-mcp" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
The fastest path to AI-powered full stack observability, even for lean teams.
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!