google-slides — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited google-slides (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
This skill explains the canonical way to drive the google-slides-mcp server. Tools are exposed as mcp__google-slides__<tool> (shown below by bare name).
The Slides API has no cross-presentation slide copy, and recreating elements by hand loses styling fidelity. So the high-fidelity pattern is always:
copy_presentation) — this clones masters,layouts, theme and color scheme, and brings every example slide along as a reusable palette.
duplicate_slide) within that copy.set_element_text, replace_all_text).prune_parked_slides).Editing the copy this way is functionally identical to hand-editing the showcase.
Never rebuild a slide by reading its elements and recreating them in a different deck — that drops theme/master/placeholder styling and will not be pixel-perfect. Always start from copy_presentation.login (google-slides-mcp-auth). If a tool returns "No cached Google credentials…", tell the user to run that command; do not try to work around it.
https://docs.google.com/presentation/d/<ID>/edit). If the user doesn't give you one, find it with search_presentations (see below) before asking.
search_presentations searches the user's Drive across native Google Slides and PowerPoint (`.pptx`/`.ppt`) files — use it whenever the user refers to a deck by description rather than ID:
search_presentations(order_by="createdTime desc", owned_by_me=true, max_results=1)
→ "the last presentation I created"
search_presentations(name_contains="corporate template", file_type="google_slides")
→ find a template by name
search_presentations(full_text_contains="Q3 revenue")
→ decks mentioning a topic (order_by is ignored by Drive for full-text queries)Results include fileType and directlyEditable. Only native Google Slides files can be used as a presentation_id; for a .pptx result, convert it first (the original file is left untouched):
import_presentation(file_id="<pptx_id>") → { presentationId, url }So "take my last deck and port it into our corporate template" is: search_presentations (find source, import if .pptx) → search_presentations (find template) → copy_presentation (template) → catalog_slides on both → rebuild each source slide on a duplicated template layout.
copy_presentation(source_id="<showcase_id>", title="Q3 Review")
→ { presentationId: "<deck>", url: ... } # the working deck
catalog_slides(presentation_id="<deck>")
→ [ { index, objectId, layoutObjectId, elementTypes, placeholders, title, isSkipped } ]
park_slides(presentation_id="<deck>", slide_ids=[<every example objectId>])
→ hides the palette so it doesn't clutter the in-progress deckcatalog_slides is how you choose which example slide fits each section (look at placeholders, elementTypes, and title). Keep the catalog — you'll reuse the example objectIds on later turns.
You do not have to do everything at once. Each call is independent, so "now add another three-column slide that says X" is just another pass:
duplicate_slide(presentation_id="<deck>", page_id="<3col_example_id>", insertion_index=2)
→ { newSlideId: "<slide>" }
get_page(presentation_id="<deck>", page_id="<slide>")
→ find the element objectIds you need to fill
set_element_text(presentation_id="<deck>", element_id="<col1>", text="…")
set_element_text(presentation_id="<deck>", element_id="<col2>", text="…")Filling strategy:
{{title}}, preferreplace_all_text(presentation_id, mappings={"{{title}}": "Results"}, page_ids=["<slide>"]) — scope it to the new slide so you don't rewrite the whole deck.
set_element_text per element. It makes the element's text exactlywhat you give but edits only the words that differ (2 calls: a read, then the write), so review comments on the rest stay attached; it is safe on empty elements. For a table cell pass row and column with the table's ID.
set_element_box(presentation_id, element_id, x_pt, y_pt, width_pt?, height_pt?).
reorder_slides(presentation_id, slide_ids, insertion_index).set_z_order, group_elements, ungroup_elements.batch_update(presentation_id, requests=[...]) (see below).
render_page(presentation_id="<deck>", page_id="<slide>", size="MEDIUM") → PNG to inspect
diff_pages(presentation_id="<deck>", page_a="<slide>", page_b="<example_id>")
→ mismatch ratio (0.0 = pixel-perfect) + a visual diff imageRender after meaningful edits and confirm the slide looks correct before moving on.
prune_parked_slides(presentation_id="<deck>") → deletes ALL parked slidesThis removes the hidden palette. Always do this last. If you need a layout you never instantiated, do it before pruning — once pruned, that example is gone (no cross-deck copy to bring it back).
Use these when the user mentions comments, feedback or review on a deck, or wants changes proposed rather than made. Slides has no suggestion mode, so an anchored comment is how you propose or flag something without editing the slide.
get_presentation(presentation_id="<deck>")
→ comments: {open, resolved, orphaned} and openComments per slide: is there feedback?
list_comments(presentation_id="<deck>") # add page_id to look at one slide
→ each open thread: commentId, content, replies, quote, locationsEvery location says where the comment sits: kind is slide, element, text (elementId, start/end and the text it covers), table_cells (cells with row/column) or notes (speaker notes), with slideId/slideIndex. Locate the words it refers to, change them (set_element_text, replace_all_text, or batch_update), then answer:
reply_to_comment(presentation_id="<deck>", comment_id="<id>",
content="Changed to 14% per the Q3 sheet.", action="resolve")add_comment(presentation_id, content, text="<the words>"). Other anchors: page_id (the slide), element_id (a shape or table), element_id + row + column (a table cell), notes=True + page_id (speaker notes). If a quote appears more than once the call fails and lists the candidates; pass occurrence or element_id.
without action when you only have an answer or a question.
orphaned has lost the content it was about (it keeps itsquote). Read it, and re-comment on the right place with add_comment if it still matters. Prefer set_element_text / replace_all_text over deleting and retyping text, which would orphan the comments on it.
delete_comment removes a thread (or one reply) for good; you can delete only whatyou wrote. Don't delete other people's feedback: resolve it.
named.
revisionId. For an index-based batch_update,pass the revisionId you last saw as required_revision_id; it fails instead of editing the wrong text if the deck changed (including someone adding a comment), and each write returns the new revision.
batch_updatebatch_update takes a list of raw Slides API request objects and applies them atomically (one write quota unit no matter how many sub-requests). Use it for operations without a dedicated tool, e.g. styling:
batch_update(presentation_id="<deck>", requests=[
{ "updateTextStyle": {
"objectId": "<el>", "textRange": {"type": "ALL"},
"style": {"bold": true, "foregroundColor": {"opaqueColor": {"themeColor": "ACCENT1"}}},
"fields": "bold,foregroundColor" } }
])updateShapeProperties, updateTextStyle, and updateSlideProperties require a `fields` mask — the tool warns if it's missing. Reference for request shapes: https://developers.google.com/workspace/slides/api/reference/rest/v1/presentations/request
Every tool makes a bounded number of API calls and returns trimmed output. To keep a session cheap and predictable:
list_slides / catalog_slides to find IDs; only get_page(..., raw=True)or get_presentation(..., raw=True) when you genuinely need full detail (raw responses leave comments out; use list_comments).
batch_update rather than many small calls.render_page / diff_pages hit the expensive thumbnail quota (300/min perproject, 60/min per user). Render specific pages you changed — never loop over a whole deck. Prefer MEDIUM size unless you need fine detail.
google-slides-mcp-auth.drive scope; user should re-run auth after enabling it on the consent screen.
re-run the auth command (or publish the consent screen).
example (full fidelity), not rebuilt it from scratch; render and diff_pages against the original example to see the delta.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.