# Google Slides MCP [Health: Active]

**Category:** 🏢 Workplace & Productivity  
**Repository:** https://github.com/A1-x-Tech/mcp-google-slides  
**GitHub Stars:** 1  
**npm Downloads (last month):** 1008  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/google-slides-mcp

## Description
MCP server for the Google Slides API: presentations, slides, text, shapes, images and tables.

## Tools
Capabilities this server exposes over MCP:

- **auth_status** — Shows whether this server is connected to Google: token presence and source (env variables or a stored in-chat login), expiry, the Google account email, granted vs missing OAuth scopes, where the credentials file lives and where the OAuth client comes from. Makes no network calls and never returns the token itself. Call it first when other tools report the server is not connected.
- **setup_instructions** — Step-by-step checklist for connecting this server to Google: creating a Google Cloud project and a Desktop-app OAuth client, publishing the consent screen (mandatory — Testing-mode refresh tokens die after 7 days), downloading the client JSON and handing its PATH to set_client. Works without any credentials; the checklist shortens to 'enable the API + log in' when an OAuth client is already configured (one client serves the whole mcp-google-* line). Never asks the user to paste secrets into the chat.
- **set_client** — Saves the OAuth client credentials from the JSON file downloaded from Google Cloud Console ('Download JSON' on a Desktop-app client). Pass the file PATH — the secret must never be pasted into the chat. The client is stored once in the shared ~/.config/mcp-google-auth/client.json (owner-only) and reused by every mcp-google-* server; tokens stay per-server. After this, call start_login.
- **start_login** — First step of connecting from the chat, without editing config files or restarting the client. Returns authorizeUrl — show it to the user as a clickable link and ask them to open it in the browser ON THIS MACHINE, pick the Google account and approve access. A one-shot listener on 127.0.0.1 catches Google's redirect; the code is exchanged locally and never passes through the chat. Does not open the browser itself. The attempt lives 10 minutes; when the browser shows the success page, call finish_login.
- **finish_login** — Second step: waits up to 20 s for the browser consent to finish, saves the tokens to an owner-only file and verifies the login with a read-only identity call, returning the account email and the granted scopes. After success every tool works immediately — no client restart. If the user granted only part of the requested permissions, the login is still saved and missingScopes lists what will not work. Logging in under a different Google account replaces the previous login (its refresh token is revoked best-effort) and the response carries previousAccountEmail so the change never goes unnoticed.
- **logout** — Revokes the stored token at Google (oauth2.googleapis.com/revoke) and deletes the local credentials file. Tokens supplied via env variables are NOT touched — remove them from the MCP client config manually; envTokenStillSet in the response says whether any are still in effect.
- **create_presentation** — Creates a new Google Slides presentation and returns it (presentationId, revisionId, pageSize, the initial title slide with its object ids, layouts and masters). The API honors only the title at creation — add slides with add_slide, content with the text/shape/table/image tools. The new deck starts with one title slide and belongs to the authorized user's Drive. Note: this server cannot delete a presentation; removing the file is a Drive operation outside its scope.
- **get_presentation** — Returns the raw presentation JSON: slides[] with every page element (shapes, text runs, tables, images), layouts[], masters[], notesMaster, pageSize (EMU: 1 pt = 12700 EMU) and revisionId. WARNING: a full deck is often hundreds of kilobytes — prefer list_slides for an inventory, and pass fields (a Google fields mask, e.g. "slides(objectId,pageElements(objectId,shape(text)))") to trim this response when you need raw detail.
- **list_slides** — Compact inventory of the deck — the read to start from: presentationId, title, revisionId, pageSizePt, slideCount and per slide its 0-based index, objectId, layoutObjectId, speakerNotesObjectId and elements[] with each element's objectId, type (shape type / table RxC / image / video / line / group), placeholder role and visible text (truncated to 160 chars). All mutation tools address these object ids, so call this before editing; per-element geometry and full text need get_presentation or get_page.
- **get_page** — Returns one page in full by its object id — works for slides, layouts, masters and notes pages alike. For a slide the response includes slideProperties.notesPage (the speaker-notes page) and every pageElement with its size, transform (EMU: 1 pt = 12700 EMU) and complete text. Use it when list_slides' summary is not enough for one page but the whole deck via get_presentation would be too much.
- **batch_update** — Sends raw Slides API batchUpdate requests for anything the typed tools don't cover: updateShapeProperties (fill, outline, shadow), updatePageProperties (slide background), updateParagraphStyle, createParagraphBullets, updateTableCellProperties, mergeTableCells, createVideo, createLine, groupObjects, updateSlideProperties and the rest of the request union. requests is the API's requests[] array verbatim, e.g. [{"updateShapeProperties":{"objectId":"...","shapeProperties":{...},"fields":"shapeFill"}}]. The batch is atomic: one invalid request rejects the whole batch and nothing is applied. Lengths/coordinates take {"magnitude":N,"unit":"PT"}; wire enums are UPPER_CASE.
- **add_slide** — Adds a slide (batchUpdate createSlide) and returns the new slide's objectId in the reply. Pick the look with either predefined_layout (BLANK, TITLE, TITLE_AND_BODY, SECTION_HEADER, ...) or layout_object_id from list_layouts — not both; with neither the slide copies the layout of the current last slide. insertion_index is the 0-based position (omitted = append at the end). Placeholders inherited from the layout arrive empty — find their object ids via list_slides, then fill them with set_text.
- **duplicate_slide** — Duplicates a slide (batchUpdate duplicateObject) — the copy, with all its elements and speaker notes, lands immediately after the original and its new objectId comes back in the reply. Works on any page element id too (the copy lands on the same slide, slightly offset). Move the copy with move_slides afterwards if needed.
- **move_slides** — Moves one or more slides to a new position (batchUpdate updateSlidesPosition). slide_object_ids must appear in their current deck order with no duplicates; insertion_index is the 0-based position BEFORE the move is applied (moving a slide down: index counts with the slide still in its old place). Use list_slides to see the current order first.
- **delete_object** — Deletes a slide or a page element (shape, image, table, video, line, group) by object id — batchUpdate deleteObject. Deleting a slide removes all its elements and speaker notes; deleting a group removes its children. There is no undo through the API (version history lives in the Slides UI). It cannot delete the whole presentation — that is a Drive file operation outside this server.
- **list_layouts** — Lists the presentation's theme building blocks: layouts[] (objectId, name like TITLE_AND_BODY, displayName, masterObjectId), masters[] and the notesMasterObjectId. Layout object ids feed add_slide's layout_object_id; the Slides API cannot import a new theme or change the master set — themes are chosen in the UI, this tool only reads what the deck already has.
- **insert_text** — Inserts text into a shape or a table cell (batchUpdate insertText) at a 0-based character index (default 0 = the beginning). For a table cell pass row_index + column_index together. Text carries \n for new paragraphs; the target must be a shape that can hold text or a table — images and lines cannot. To REPLACE what's already there use set_text instead; to fill an empty layout placeholder this works directly (find its object id via list_slides).
- **set_text** — Replaces the ENTIRE text of a shape or table cell: one atomic batch of deleteText(ALL) + insertText. If the target has no text yet the delete is skipped automatically, so it works on empty placeholders too. An empty text just clears the object. Replacing text drops per-character styling — restyle with update_text_style afterwards if needed. For a table cell pass row_index + column_index together.
- **replace_text** — Finds and replaces every occurrence of a string across the whole deck — shapes and table cells on slides, and their speaker notes (batchUpdate replaceAllText). Scope it with page_object_ids to touch only specific slides. Matching is literal (no regex), case-sensitive by default (match_case=false to ignore case). Returns occurrencesChanged in the reply — 0 means nothing matched, check the exact wording with list_slides. The classic template flow: duplicate a template deck, then replace {{placeholders}}.
- **update_text_style** — Styles the text of a shape or table cell (batchUpdate updateTextStyle): bold, italic, underline, strikethrough, font_family (e.g. "Roboto"), font_size_pt, foreground_color (#RRGGBB) and link_url. Only the provided fields change (the field mask is computed automatically); at least one is required. By default the whole text is styled; pass start_index + end_index (0-based character offsets, end exclusive) to style a range — get the offsets from get_page's textElements. For a table cell add row_index + column_index.
- **create_shape** — Creates a shape on a slide (batchUpdate createShape), optionally with its initial text in the same atomic batch. Types: text_box, rectangle, round_rectangle, ellipse, diamond, triangle, right_arrow, left_arrow, up_arrow, down_arrow, star, heart, cloud, callout — the API's other ~130 ShapeTypes go through batch_update. Position/size are in points ((0,0) is top-left; a default 16:9 slide is 720x405 pt); omitted geometry defaults to 300x80 pt at (50,50). Returns the shape's objectId in the reply. Fill/outline/shadow are not parameters here — style via batch_update updateShapeProperties; text styling via update_text_style.
- **create_image** — Inserts an image on a slide (batchUpdate createImage) from EITHER image_url — a public https URL Google's servers can fetch (validated: https-only, no private/local hosts, ≤2 kB; the image itself must be PNG/JPEG/GIF, ≤50 MB, ≤25 megapixels) — OR image_path, a local file that is checked by magic bytes, uploaded to Drive, shared by link for the fetch and DELETED from Drive right after (Slides stores its own copy, so the temp file is disposable; the result reports the cleanup). Omitted size keeps the image's native proportions fitted to the page; position/size are in points. Returns the image objectId in the reply.
- **replace_image** — Swaps the bitmap of an existing image element while keeping its frame, position and effects (batchUpdate replaceImage). The new picture comes from image_url or image_path with exactly the same validation/upload/cleanup rules as create_image. replace_method: center_inside (default — scales to fit inside the frame, keeping aspect) or center_crop (fills the frame, cropping the overflow).
- **update_transform** — Moves and/or scales a page element (batchUpdate updatePageElementTransform). mode=absolute REPLACES the element's whole transform: translate_x_pt/translate_y_pt become the new top-left-ish origin and omitted scales default to 1 — NOT to the current values, so an absolute move of a scaled element also resets its scale unless you resend it. mode=relative multiplies onto the existing transform: translate shifts by the given points, scale 2 doubles the current size (scaling happens around the origin, which also shifts the element — for a simple move prefer relative translate only). Element geometry to reason from is in get_page.
- **create_table** — Creates an empty rows x columns table on a slide (batchUpdate createTable) and returns its objectId in the reply. Fill cells with set_text / insert_text using row_index + column_index (0-based); restructure later with edit_table. Position/size are in points; omitted geometry lets the API pick a centered default. Cell styling (borders, background) goes through batch_update updateTableCellProperties.
- **edit_table** — Restructures an existing table in one call. action=insert_rows needs row_index (+optional below, default true, and count 1-20); insert_columns needs column_index (+optional right, default true, and count); delete_row needs row_index and delete_column needs column_index — deletion permanently discards the cells' content, and the remaining rows/columns shift so indexes change between successive edits (re-check with list_slides). Cell TEXT is written with set_text/insert_text (row_index + column_index), not here.
- **get_speaker_notes** — Returns a slide's speaker notes as plain text, plus the speakerNotesObjectId (the text shape on the slide's hidden notes page). The lookup fetches the slide's notes page internally, so you only need the slide's object id from list_slides. An empty string means the slide has no notes yet. Notes text is also searched by replace_text.
- **set_speaker_notes** — Replaces a slide's speaker notes with the given text (empty string clears them). The tool resolves the slide's speakerNotesObjectId internally and rewrites that shape atomically — you pass the slide's object id, not the notes shape id. Notes are visible in the presenter view and exported by format txt; use \n for paragraphs.
- **manage_comments** — Comment threads on the presentation file (served by the Drive API internally — scoped to this one file, no generic Drive access). action=list returns comments with replies and resolved status (pageSize/page_token to paginate, include_deleted for tombstones); get needs comment_id; create needs content and starts a new unanchored thread (the API cannot anchor a new comment to a specific slide or shape); reply needs comment_id + content and/or resolve=true (resolve closes the thread); delete needs comment_id and permanently removes the thread with all replies. Requires a Drive scope on the OAuth token (https://www.googleapis.com/auth/drive) — a presentations-only token gets a 403 here.
- **get_thumbnail** — Renders one page (slide, layout or master) as a PNG. Without output_path it returns contentUrl + width/height — the URL is short-lived (~30 minutes) and served from googleusercontent.com. With output_path the PNG is downloaded and saved to that local file instead (the download is restricted to Google's image hosts). size: large (1600px, default), medium (800px) or small (200px). This is the way to SEE what a slide actually looks like — the JSON never shows rendering. Counts as an expensive read in the API quota.
- **export_presentation** — Downloads the whole presentation as pdf, pptx, odp or txt (txt = all text incl. speaker notes) and saves it to output_path, overwriting an existing file. Served by the Drive export endpoint internally, which caps exports at 10 MB — a bigger deck fails with an export-size error (split it or use the Slides UI). Requires a Drive scope on the OAuth token (drive.readonly is enough; presentations-only gets a 403). Returns saved_to, bytes and mime_type; nothing in the presentation changes.
- **raw_request** — Escape hatch to call any Google Slides API v1 path directly, for requests even batch_update doesn't cover — e.g. GET "v1/presentations/<id>/pages/<pageId>" with a custom fields mask in the query string, or a POST with a body shaped differently from the typed tools. The Slides API only has GET and POST endpoints; the path is relative to https://slides.googleapis.com and may carry a query string. The Bearer token is added automatically; a path resolving to a foreign origin is rejected. Drive endpoints (comments, export) are NOT reachable here — use manage_comments / export_presentation.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

```json
"mcpServers": {
  "google-slides-mcp": {
    "command": "npx",
    "args": ["-y","@a1-x-tech/mcp-google-slides@latest"]
  }
}
```

## Documentation

## What Google Slides MCP does

Google Slides MCP exposes Google Slides presentation operations as MCP tools for an AI application. It can inspect decks and pages, create presentations, add or rearrange slides, and modify text, shapes, images, and tables. It also covers speaker notes, comment threads, thumbnails, and PDF or PPTX export.

The server works with the contents of a presentation rather than acting as a general-purpose Drive manager. It does not provide tools to list, share, rename, or delete Drive files. Drive access is limited to operations that need it for the selected presentation, such as comments, exports, and local-image uploads.

## How it works

Google Slides objects are addressed by explicit object IDs. A useful first step is `list_slides`, which provides a compact view of slide positions, layouts, elements, and visible text before an edit is attempted.

Presentation edits are sent through the Slides API's atomic `batchUpdate` mechanism. If one request in a batch is invalid, the whole batch is rejected rather than leaving a partially modified deck. Tool inputs use points for measurements, while the API stores dimensions in EMU; the server converts between those representations.

Creating a presentation produces a deck with one title slide, and only the title is applied during creation. Existing layouts can be instantiated, but themes cannot be imported. The server can also render slide thumbnails, though generated thumbnail URLs expire after roughly 30 minutes.

## Setup and configuration

Node.js 20 or newer and a Google account are required. Install the npm package as an MCP stdio server with:

```bash
npx -y @a1-x-tech/mcp-google-slides@latest
```

Add that command to an MCP-compatible client. The documented client configurations use stdio and include Codex, Claude Code, Claude Desktop, Cursor, and VS Code.

After adding the server, tell the assistant to connect Google Slides. The authorization flow guides the user through creating an OAuth client and approving access. It uses PKCE and receives Google's redirect on `127.0.0.1`; tokens are retained by the server, so credentials do not need to be entered during installation or placed in a configuration file for the conversational setup.

The primary Google scope is `presentations`. Drive scopes are used only when required for local images, comments, or exports.

## Tools and capabilities

The 32 tools cover these areas:

- Inspect presentations, slides, page elements, notes, and comments.
- Create, duplicate, move, and delete slides.
- Add and transform text boxes, shapes, images, and tables.
- Insert or replace images from URLs or local files.
- Write, replace, style, and find or replace text across a deck.
- Modify speaker notes and table structure.
- List, reply to, resolve, and delete comment threads.
- Render thumbnails and export presentations as PDF or PPTX.
- Send raw `batchUpdate` or raw API requests when a dedicated operation is insufficient.

## Limitations and notes

The Slides API cannot delete, rename, or share the presentation file, because those are Drive file operations outside the server's scope. New comments cannot be anchored to a specific slide. Exported files are limited to 10 MB, and deleting slides, elements, or comment threads has no API-level undo.

Find-and-replace changes every matching occurrence in the deck. Raw API and raw batch-update tools can also perform operations beyond the dedicated tools, so they may be destructive. A confirmation boundary is appropriate for creating decks, changing content, broad replacements, and deletions, while inspection, thumbnail rendering, and export do not modify the presentation.

_Full upstream README: https://allmcps.com/mcp/google-slides-mcp/readme_

