Sno Station
Sno Station is your agents' workstation: open-source software that turns the AI agents you already run into one squad on your own machine. Two heads are better than one, and they get better with every
An MCP server that lets AI assistants edit open PowerPoint presentations on macOS and the web
AI summary · README
PluginsTypeScript · ShellGitHub @kzarzyckiSelf-installOwner unconfirmed
Paste into Claude or ChatGPT for installation instructions suited to your environment.
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Installable projects can be verified without a deployment domain. Use GitHub sign-in for a personal repository, or the repository verification file above for an organization repository.
Ask about other verification options
Loading the full README. The saved excerpt is shown below.
An MCP server that lets AI assistants manipulate live, open PowerPoint presentations on macOS and PowerPoint Web via Office.js APIs.
Unlike file-based tools (python-pptx), PowerPoint MCP works with presentations that are already open — changes appear instantly, and you keep full access to PowerPoint's UI, animations, and formatting.
This repository includes Codex plugin metadata in .codex-plugin/ and Codex repo instructions in AGENTS.md.
The Codex plugin MCP config in .mcp.json uses the shared HTTP MCP endpoint:Read the full README ↗An MCP server that lets AI assistants manipulate live, open PowerPoint presentations on macOS and PowerPoint Web via Office.js APIs.
Unlike file-based tools (python-pptx), PowerPoint MCP works with presentations that are already open — changes appear instantly, and you keep full access to PowerPoint's UI, animations, and formatting.
This repository includes Codex plugin metadata in .codex-plugin/ and Codex repo instructions in AGENTS.md.
For local development:
git clone https://github.com/kzarzycki/powerpoint-mcp.git
cd powerpoint-mcp
npm install
npm run build
The Codex plugin MCP config in .mcp.json uses the shared HTTP MCP endpoint:
{
"mcpServers": {
"powerpoint-mcp": {
"type": "http",
"url": "http://localhost:3001/mcp"
}
}
}
The Codex plugin ships a SessionStart hook in hooks/hooks.json. On session start it runs scripts/start-http-bridge.sh, which starts the built bridge with node ./dist/index.cjs --http --bridge if the HTTP MCP server or add-in bridge is not already healthy.
Then restart PowerPoint, open a presentation, and click the bridge add-in in the ribbon. For manual local development, start the TypeScript source directly with node --experimental-strip-types ./server/index.ts --http --bridge.
Zero-config install from the marketplace. MCP auto-starts, add-in auto-sideloads, skill included.
/plugin marketplace add kzarzycki/powerpoint-mcp
/plugin install powerpoint-mcp@powerpoint-mcp
Then restart PowerPoint, open a presentation, and click the bridge add-in in the ribbon.
Build and install as a one-click .mcpb extension (from source):
git clone https://github.com/kzarzycki/powerpoint-mcp.git
cd powerpoint-mcp
npm install && npm run build:mcpb
open powerpoint-mcp-*.mcpb # opens Claude Desktop installer
The extension auto-starts the bridge and auto-sideloads the add-in. Restart PowerPoint after first install.
Known limitations:
git clone https://github.com/kzarzycki/powerpoint-mcp.git
cd powerpoint-mcp
npm install
npm run sideload # copies manifest to PowerPoint's add-in folder
npm start -- --http --bridge # starts the MCP HTTP transport (:3001/mcp) + add-in bridge (:8080)
Then point your MCP client at the HTTP transport. Create or merge into the project's .mcp.json:
{
"mcpServers": {
"powerpoint-mcp": {
"type": "http",
"url": "http://localhost:3001/mcp"
}
}
}
Restart PowerPoint, open a presentation, and click the bridge add-in in the ribbon. For STDIO mode and other standalone options, see skills/powerpoint-mcp/references/setup.md.
This project was inspired by the Claude in PowerPoint add-in. The first time I tried it, I was amazed — it edits live, open decks via Office.js, and the results are far better than file-based pptx tools. But it only works inside the add-in, which means no access to CLAUDE.md, skills, or any other Claude Code features. PowerPoint MCP brings those same Office.js capabilities to Claude Code (and any MCP client) so you get live editing with the full power of your coding environment.
The project was originally named powerpoint-bridge and renamed to powerpoint-mcp in v0.5.0. The "bridge server" component keeps its name — it bridges MCP and the Office.js add-in over WebSocket.
AI Assistant <--MCP STDIO/HTTP--> Bridge Server (Node.js) <--WS/WSS--> PowerPoint Add-in (Office.js)
| |
STDIO (default) Desktop: WKWebView sandbox
or HTTP (:3001/mcp) Web: browser iframe
localhost:8080 (HTTP) Office.js API 1.1-1.8
or :8443 (HTTPS) executes commands on
serves add-in files + WS live presentation
Two MCP transports are supported:
--stdio flag; the MCP client spawns the server process directly127.0.0.1:3001/mcp; enabled by the --http flag (e.g. node server/index.ts --http --bridge for development with .mcp.json)Three components in one repo:
addin/ — Office.js taskpane add-in that loads inside PowerPoint and connects as a WebSocket clientserver/ — Node.js bridge server: HTTP + WS + MCP transport (HTTPS/WSS opt-in via BRIDGE_TLS=1)skills/powerpoint-mcp/ — Claude Code skill with tool docs, code patterns, and setup guide (auto-installed with plugin)certs/ — Optional local TLS certificates for HTTPS mode (generated, gitignored)brew install node
| Tool | Description |
|---|---|
list_presentations | Lists all connected presentations with their IDs and status |
inspect_deck | Deck overview: slide dimensions, theme (colors + fonts), and all slides with layout and shape count |
inspect_layouts | Slide layouts with names, types, placeholders, and positions. Supports field selection |
inspect_slide | Returns detailed shape info for a slide (text, positions, sizes, fills) |
scan_slide | Lightweight shape scanner: IDs, types, and positions |
screenshot_slide | Captures a visual screenshot of a slide as PNG (requires PowerPoint 16.96+) |
preview_deck | Returns thumbnails + text for all/selected slides in one call (efficient full-deck review) |
copy_slides | Copies slides between two open presentations (data stays server-side, never in Claude context) |
insert_image | Inserts an image from a file path, URL, or base64 data onto a slide |
get_local_copy | Returns a local file path for the presentation (passthrough for local, exports cloud files to temp .pptx) |
search_fluent_icons | Search Microsoft Fluent UI icons by keyword and insert as SVG |
execute_officejs | Runs arbitrary Office.js code inside the live presentation |
add_slide | Add a new slide from a named layout at a specific position, with placeholder text pre-filled |
duplicate_slide | Duplicate a slide within the same presentation; data stays server-side |
read_deck_text | Lightweight text extractor: slide titles and body text as plain strings (~20x smaller than inspect_slide) |
read_shape_paragraphs | Read raw OOXML <a:p> paragraphs from a shape's text body, preserving all formatting |
edit_shape_paragraphs | Replace paragraph content of a shape with raw OOXML <a:p> XML |
read_slide_xml |
When multiple presentations are open, pass presentationId (from list_presentations) to target a specific one.
insert_image tool), not shape API; positioning works but no shape-level manipulation after insertionedit_slide_chart), not via Office.js chart APIPowerPoint MCP assumes a single-user, locally-trusted machine. Both network servers bind to the loopback interface only:
127.0.0.1:8080 (or 127.0.0.1:8443 with BRIDGE_TLS=1)127.0.0.1:3001; the default STDIO transport opens no network portHost header is not 127.0.0.1:3001 / localhost:3001)/mcp request bodies to 16 MiB (16,777,216 bytes), enforced while the body streams in rather than after it is fully buffered. The ceiling covers the largest supported request — a base64-encoded image via insert_image — with headroom: base64 inflates raw bytes by ~4/3, so 16 MiB admits a ~12 MB source image plus the surrounding JSON-RPC envelope. An oversized or over-length chunked upload gets a 413 response (Request body too large) instead of being buffered into memory; the connection remains usable for the next request.GET https://registry.npmjs.org/powerpoint-mcp/latest to check for a newer version. Nothing is sent beyond the request itself; no presentation data, telemetry, or identifiers. Disable it with BRIDGE_NO_UPDATE_CHECK=1 or the --no-update-check flag.Local file and URL access. The insert_image tool reads arbitrary local file paths (sourceType: 'file') and fetches arbitrary URLs (sourceType: 'url') with the full privileges of the server process. A URL fetch can reach internal/loopback addresses (SSRF), and a file read can pull in any file the user can read. Only run the bridge on a trusted, single-user machine.
Local-trust posture. The add-in executes whatever JavaScript the bridge sends it over the local WebSocket. The bridge WebSocket enforces an Origin allowlist: it only accepts the upgrade when the request's Origin header exactly matches the bridge's own currently-serving origin (http://localhost:PORT, or https://localhost:PORT with BRIDGE_TLS=1) — the origin the add-in's own served page always connects from, on both PowerPoint for Mac and PowerPoint Web (the WAC host frame at officeapps.live.com never opens this socket itself; the taskpane iframe it hosts navigates directly to the bridge origin first). A missing, malformed, or foreign Origin is rejected before the socket can register or answer any pending request — a real browser/webview always sends Origin on a WebSocket handshake, so no legitimate client is excluded. This is an origin policy, not authentication: it stops a foreign page's script from driving the bridge, but any local process that can reach the loopback port and mimic that Origin header still can. Do not run PowerPoint MCP on a shared or multi-user host.
execute_officejs runs arbitrary code inside PowerPoint's Office.js runtime. This is by design — it gives the AI full access to the Office.js API. Only use this with MCP clients you trust.
Planned hardening. Before binding to any non-loopback interface or shipping a remote-MCP deployment, the bridge needs a per-session handshake token and an authenticated transport.
Add-in not appearing in PowerPoint
npm run sideload and restart PowerPoint~/Library/Containers/com.microsoft.Powerpoint/Data/Documents/wef/manifest.xmlAdd-in shows "Disconnected"
Make sure the bridge server is running. In plugin mode the server auto-starts with Claude Code — call any tool to verify. For standalone installs, run npm start -- --bridge and verify with curl http://127.0.0.1:8080/health. The add-in auto-reconnects with exponential backoff.
Using HTTPS mode (required for PowerPoint Web) HTTPS is required for PowerPoint Web and optional for desktop. To enable:
brew install mkcert && mkcert -install (one-time, trusts the CA in browsers)npm run setup-certs to generate localhost certificatesnpm run sideload:https and restart PowerPointaddin/manifest-https.xmlBRIDGE_TLS=1 npm start -- --bridge| Platform | Status |
|---|---|
| macOS | Supported (primary) |
| PowerPoint Web | Supported — requires HTTPS mode, sideload via browser (all tools work) |
| Windows | Untested — different sideloading path |
| Linux | Not supported (no PowerPoint for Linux) |
The add-in uses Office.AutoShowTaskpaneWithDocument to minimize manual activation:
| Platform | Behavior |
|---|---|
| macOS desktop | Taskpane auto-opens when the add-in is sideloaded. Closing the pane keeps the WebSocket connection alive (shared runtime with lifetime="long"). |
| PowerPoint Web | One ribbon click per document per browser session to activate. After activation, the pane auto-reopens on page reload. Closing the pane keeps the connection alive within the session. |
| New file from template | OOXML template embedding can pre-activate the add-in (see below) |
Requires SharedRuntime 1.1 (PowerPoint 16.46+ on Mac, 2102+ on Windows). Centralized Deployment via Microsoft 365 admin center can eliminate the manual click for organizational use.
External tools can embed the add-in reference directly into .pptx templates so the add-in activates on first open without prior installation. This requires injecting two OOXML parts into the .pptx zip:
ppt/webextensions/webextension1.xml — add-in reference with Office.AutoShowTaskpaneWithDocument propertyppt/webextensions/taskpane.xml — taskpane configuration (dock state, visibility, width)Plus the corresponding entries in [Content_Types].xml and relationship files.
Set visibility="0" if the add-in must already be sideloaded; set visibility="1" to prompt users to install it on first open.
For a complete working example, see Office-OOXML-EmbedAddin. The add-in ID to reference is AE89909C-2813-4B08-9E1B-49E7379BD0E6 with storeType="Registry" and store="developer" for sideloaded installations.
See CONTRIBUTING.md for development setup and guidelines.
| Read the full raw OOXML of a slide, or filter to a specific shape |
edit_slide_xml | Edit slide XML and reimport (xml mode or JS code mode for DOM manipulation) |
read_slide_zip | Read multiple files from the exported slide zip (slide XML, rels, chart XMLs, Content_Types) |
edit_slide_zip | Update multiple files in the slide zip and reimport in a single operation |
edit_slide_chart | Create a chart on a slide from structured data (column, bar, line, pie, doughnut, area) via OOXML |
read_speaker_notes | Read speaker notes from slides as plain text |
edit_speaker_notes | Set speaker notes on one or more slides; accepts markdown text |
format_shapes | Apply fill color and font formatting to multiple shapes on a slide in one call |
search_text | Search for text across all slides — like grep for slides; supports regex |
verify_slides | Run programmatic checks on a slide (overlaps, out-of-bounds, empty text, unused placeholders) |