Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
voog_list_sitesA

List all sites configured in the global voog.json. Returns [{name, host}, ...]. Call this first to see what sites are available before invoking any other voog_* tool. If a site you just registered is missing, call voog_reload_config.

voog_reload_configA

Re-read the global voog.json (and its .env) and drop cached clients, so sites registered AFTER this server started become usable without restarting the MCP host. Returns the current site list plus what was added/removed.

Use when 'unknown site' comes back for a site you just added, or after rotating a token in .env. Running tool calls are unaffected — they finish against the client they already hold. A malformed config leaves the current one in place.

articles_listA

List blog articles on the Voog site (simplified: id, title, path, public_url, published, published_at, updated_at, created_at, language_code, page_id). All filters optional. Read-only.

article_getA

Get full article details by id (title, path, body, autosaved_*, published_at, language, page, data, image, tags). Read-only.

article_createA

Create a new blog article. Required: page_id (the parent blog page), title. Optional: body (HTML), excerpt, description (meta), path (auto from title if omitted), image_id, tag_names (array), data (custom dict), publish (default false). Title and body go to autosaved_* fields per Voog convention; if publish=true, publishing:true is set so values copy to published fields atomically. NOT idempotent — repeat calls create multiple articles.

article_updateA

Update an existing article. Title/body/excerpt go to autosaved_* per Voog convention (the public fields are read-only — call article_publish to push autosaved → published). description/path/image_id/tag_names/data are non-autosaved fields and update directly. At least one field must be supplied.

data field is sent via PATCH (merge semantics) — only the keys you pass are touched. To delete a key, use article_delete_data. Calls without data route via PUT as before.

article_publishA

Publish an article. Voog only copies autosaved_* → published fields when publishing:true is sent in the SAME PUT as the autosaved values — that's why this needs a separate tool rather than a publish flag on article_update.

Two modes:

  1. FAST PATH (recommended) — pass ALL THREE autosaved_title, autosaved_body, autosaved_excerpt args. Tool issues a single PUT atomically; no race window.

  2. FALLBACK — pass none of them. Tool does GET to fetch current autosaved_* values then PUTs them back with publishing:true. There is a small race window between the GET and the PUT — if the article is edited concurrently, the publish may capture a stale snapshot.

Mixed (some autosaved_* provided, some not) is rejected — the caller must be explicit.

article_deleteB

Delete an article. IRREVERSIBLE — Voog does not retain deleted articles. Requires force=true.

article_set_dataA

Set a single article.data. value (PUT /articles/{id}/data/{key}). To delete a key use article_delete_data. Keys starting with 'internal_' are server-protected and rejected client-side.

article_delete_dataA

Delete a single article.data. (DELETE /articles/{id}/data/{key}). IRREVERSIBLE — the key is removed permanently. Requires force=true; without it the call is rejected. Keys starting with 'internal_' are server-protected and rejected client-side.

asset_uploadA

Upload local image files into the site's media library (POST /assets -> PUT bytes -> PUT confirm) WITHOUT attaching them to anything. Use for images referenced from site.data / page.data maps, article galleries or template markup; use product_set_images when the images are a product's gallery.

Returns each asset's id, dimensions, public path (/photos/) and the derivative sizes Voog actually produced — build srcsets from those widths, never from guessed ones (a width Voog did not make answers 403 and the browser renders nothing). Voog caps the LONG side of each derivative at 150/600/1280/2048 and scales the other side to match the source aspect ratio, so a wide image's derivatives are capped on WIDTH, not height.

By default an existing asset with the same filename is REUSED rather than uploaded again: Voog auto-suffixes duplicate filenames (photo-1.jpg), so re-uploading silently orphans the original. Pass allow_duplicate=true to force a new asset (e.g. a corrected re-shoot under a fresh sequence letter).

Waits for Voog to finish its async resizes before returning — up to 120s per file, polling the API every 5s, so a large batch is slow by design. Each result carries sizes_complete: false means the wait timed out and sizes is PARTIAL, so build the srcset from a later read rather than from those widths. Pass wait_for_sizes=false to skip the wait entirely (then sizes is empty).

Do NOT request a derivative URL over HTTP to check whether it exists — a too-early request gets a 403 that the CDN caches for ~1h, breaking a URL that was about to work.

cart_rules_listB

List all cart rules (GET /admin/api/ecommerce/v1/cart_rules). Read-only.

cart_rule_getB

Get a single cart rule by id (GET /admin/api/ecommerce/v1/cart_rules/{id}). Read-only.

cart_rule_createA

Create a cart rule (POST /admin/api/ecommerce/v1/cart_rules). Envelope {cart_rule: {...}}. Required: kind, target_kind, target_id, conditions[], result{}. Inner conditions[] entries are {value, comparator, field, value_type}. result is {value, field, value_type}. Inner key validation is left to Voog — invalid combos return a 422 with the offending field.

cart_rule_updateB

Update a cart rule (PUT /admin/api/ecommerce/v1/cart_rules/{id}). Envelope {cart_rule: {...}}. Partial — at least one field must be supplied. Common partial updates: enabled, position.

cart_rule_deleteA

Delete a cart rule (DELETE /admin/api/ecommerce/v1/cart_rules/{id}). Requires force=true. Past orders that already had the rule applied are not affected.

categories_listA

List all ecommerce product categories (GET /admin/api/ecommerce/v1/categories). Read-only. Each entry has id, name, slug, parent_id, depth, created_at, updated_at. Use category.id from the results as the products_list(category_id=...) filter.

category_getA

Get a single category by id (GET /admin/api/ecommerce/v1/categories/{id}). Read-only.

category_createA

Create a category (POST /admin/api/ecommerce/v1/categories). Envelope: {category: {...}}. Required: name. Optional: slug (auto-generated if omitted), parent_id (for sub-categories). NOTE: Voog does not support description / image_id on categories despite some doc pages suggesting otherwise — verified empirically 2026-05-27.

category_updateA

Update a category (PUT /admin/api/ecommerce/v1/categories/{id}). Envelope: {category: {...}}. Partial — at least one of name / slug / parent_id must be supplied.

category_deleteA

Delete a category (DELETE /admin/api/ecommerce/v1/categories/{id}). Requires force=true. Products in the category are NOT deleted; they're orphaned from the category. Voog may reject if the category has child categories.

site_cloneA

Copy one Voog site's content onto another (layouts, layout assets, media, site settings, pages, content areas, articles). site is the SOURCE (read-only); target_site is OVERWRITTEN. Both must be names from voog_list_sites.

DRY RUN BY DEFAULT — without force=true nothing is written and the result reports what would happen. Run phase 'plan' first: it returns source object counts, the target's remaining asset quota, and the specific things Voog will not let a clone reproduce.

RESUMABLE. state_dir holds source-id -> target-id maps that make every phase re-runnable: a run stopped by an asset quota or a transient error is continued by calling again with the same state_dir, and nothing is created twice. Use a FRESH state_dir per source/target pair — the tool refuses a directory that belongs to a different pair.

Phases run in dependency order regardless of the order you list them (a page cannot reference a layout that does not exist yet): layouts, layout_assets, assets, site, pages, contents, articles, cleanup, verify.

NOT COPIED: ecommerce (products, categories, discounts, cart rules, orders), elements, redirects, webhooks, forms, comments. Languages are matched by code, never created.

Voog limits the clone cannot work around: created_at is not settable — PUT returns 200 and the value resets to now, so every copied article and page carries the clone date. published_at is not settable, for the same reason. Duplicate article paths cannot be reproduced — Voog now auto-suffixes a colliding path with -1. Legacy sites that hold two articles on one path will have the twin land on a different URL. Asset storage is capped per plan. The clone checks the target's remaining quota before uploading and stops cleanly rather than hitting 422 quota_exceeded partway through.

comments_listA

List comments on an article (GET /articles/{article_id}/comments). Returns the full Voog comment shape (id, author, body, email, is_spam, created_at). Use article_id from articles_list. Read-only.

comment_deleteA

Remove a comment (DELETE /articles/{article_id}/comments/{comment_id}). Voog returns 204. Requires force=true; without it the call is rejected. Run comments_list first to confirm the id.

comment_toggle_spamA

Flip a comment's spam flag (PUT /articles/{article_id}/comments/{comment_id}). Body is FLAT: {is_spam: bool}. Voog's moderation UI also uses this endpoint. For other field edits use voog_admin_api_call (rare).

content_partial_updateA

Update a content partial (PUT /content_partials/{id}). Content partials are reusable template fragments embedded in pages and layouts. PUT body is flat (no envelope). At least one of body (HTML/text content) or metainfo (object) must be supplied. Update is idempotent — calling with the same payload twice has the same end state.

discounts_listA

List all ecommerce discounts (GET /admin/api/ecommerce/v1/discounts). Read-only.

discount_getA

Get a single discount by id (GET /admin/api/ecommerce/v1/discounts/{id}). Read-only.

discount_createA

Create a discount (POST /admin/api/ecommerce/v1/discounts). Envelope {discount: {...}}. Required: code. Empirically-verified enum values (rejected client-side with a clear error before round-tripping to Voog): status ∈ {open, closed} amount_mode ∈ {net, gross} discount_type ∈ {fixed, percentage} applies_to ∈ {cart, cart_and_shipping, categories, products, shipping} Optional: name, description, amount, valid_from (ISO8601), valid_to, redemption_limit, stackable, currency.

discount_updateA

Update a discount (PUT /admin/api/ecommerce/v1/discounts/{id}). Envelope {discount: {...}}. Partial — at least one discount field must be supplied. Enum-restricted fields (client-validated): status ∈ {open, closed}; amount_mode ∈ {net, gross}; discount_type ∈ {fixed, percentage}; applies_to ∈ {cart, cart_and_shipping, categories, products, shipping}.

discount_deleteA

Delete a discount (DELETE /admin/api/ecommerce/v1/discounts/{id}). Requires force=true. Already-used discount records remain on past orders; deletion only prevents future use.

ecommerce_settings_getA

Get ecommerce store settings (currency, tax_rate, value_date_days, default_language, decimal_places, company_name, bank_details, terms, privacy_policy, products_url_slug, etc.). Includes per-language translations. Read-only. Note: this is also the source of truth for price_entry_mode (net vs gross) — product tools' price fields are interpreted against this setting.

ecommerce_settings_updateA

Update ecommerce settings. attributes: flat root-level fields (currency, tax_rate, notification_email, ...). translations: nested {field: {lang: value}} for translatable settings. The set of translatable keys is discovered at runtime from GET /settings?include=translations (cached 60s per-site) — new server-side keys are picked up automatically. Wraps payload in {settings: {...}} envelope.

elements_listA

List elements (id, title, path, page_id, element_definition_id, position). Optional filters: page_id, language_id, language_code, element_definition_id, element_definition_title, page_path, page_path_prefix. Pass include_values=true to include the values hash in the projection (off by default — values clutter list views; use element_get for full shape). Read-only.

element_getA

Get a single element by id, with full values hash. Use elements_list to find ids.

element_definitions_listA

List element definitions (id, title, property_keys — the field keys each definition expects). Use the returned id for element_create.element_definition_id. Read-only. Mutating definitions (POST/PUT/DELETE) is deferred — handle via voog_admin_api_call when needed.

element_createA

Create an element (POST /elements). Body is FLAT. Required: element_definition_id OR element_definition_title (id takes precedence per Voog docs); page_id; title. Optional: path (auto-generated from title if omitted), values (custom-properties hash matching the element_definition's schema).

element_updateA

Update an element (PUT /elements/{id}). Partial — supply ONLY the fields to change. Body is FLAT. Updatable: title, path, values. At least one besides element_id is required.

element_deleteA

Delete an element (DELETE /elements/{id}). Voog returns 204. Requires force=true; without it the call is rejected. Run elements_list first to confirm the id.

element_moveA

Re-order or re-parent an element instance (PUT /elements/{element_id}/move). Inputs travel as QUERY-STRING params per Voog docs (mirrors node_move). All params optional; supply at least one of page_id, before, or after. page_id = new parent page id (integer); before / after = existing element id for positional placement on current or new parent page. SCOPE NOTE: this operates on element INSTANCES inside a definition, not on element_definitions (the schema). element_definition mutations remain passthrough — different resource. Use elements_list to find element ids; use element_definitions_list for schema discovery. Voog docs: https://www.voog.com/developers/api/resources/elements

layout_renameA

Rename a layout (PUT /layouts/{id} {title}). Reversible — rename back to the original title to undo. The new title must not contain '/' or '\' or start with '.'.

layout_createA

Create a new layout or component (POST /layouts). kind='layout' for full templates (defaults content_type='page'; use 'blog_article' for blog post templates); kind='component' for shared partials (content_type ignored). Returns the new id. NOT idempotent — calling twice creates two separate layouts.

asset_replaceA

Rename a layout_asset by creating a new one with the desired filename (DELETE+POST workaround — Voog API rejects PUT with filename changes). Returns both old and new ids. The OLD asset is intentionally left in place; after updating templates that reference the old filename, delete the old asset manually with DELETE /layout_assets/{old_id}.

layout_updateA

Update a layout — body (Liquid template source), title, or both. At least one must be supplied. Reversible by calling again with the previous values; idempotent. body crosses a JSON boundary, so literal \uXXXX escapes in a source file arrive already decoded; raw U+2028/U+2029 and C0 controls are refused as the fingerprint of that (issue #138). To deploy a tracked .tpl byte-exactly, use layouts_push.

layout_deleteA

Delete a layout. IRREVERSIBLE — Voog does not retain deleted layouts. Refuses without force=true.

Voog blocks deletion of layouts that still have pages assigned — the API returns an error, the layout is NOT deleted. Reassign those pages first via page_set_layout, then retry. Back up with site_snapshot before this operation.

layout_asset_createA

Create a layout_asset. TEXT content only, and data crosses a JSON boundary — literal \uXXXX escapes in a source file arrive already decoded, so raw U+2028/U+2029 and C0 controls are refused as the fingerprint of that (issue #138). Deploy a tracked file byte-exactly with layouts_push; upload binaries with layout_asset_upload. filename + asset_type + data required. asset_type ∈ {stylesheet, javascript, image, plain_text, video, pdf, ...}. For image uploads, use POST /assets + 3-step protocol via product_set_images instead — this tool is for text assets (CSS/JS/HTML fragments).

layout_asset_updateA

Update a layout_asset's content (PUT /layout_assets/{id} {data}). filename is read-only — Voog returns 500 if filename is sent on PUT. Use asset_replace to rename. NOT byte-exact for a file on disk: data crosses a JSON boundary, so literal \uXXXX escapes in the source arrive decoded and Voog stores the decoded form under a clean ✓ (issue #138). To deploy a tracked .js/.css file, use layouts_push(files=[...]) — it reads from disk. This tool refuses content carrying raw U+2028/U+2029 or C0 controls, the decode fingerprints that change what a file means.

layout_asset_uploadA

Upload a BINARY layout asset from disk — favicon, icon, font, inline image (multipart POST /layout_assets, issue #140 item 4). layout_asset_create only carries text data, so binaries previously needed a raw curl call.

Served from /images/ (or the site's asset path), not /photos — these live with the templates, not in the media library. For photos referenced from content or site.data use asset_upload instead.

Voog derives asset_type and content_type from the file; the result is editable=false (no text body to edit). Uploading the same filename again creates a SECOND asset — delete the old one, or use asset_replace semantics, if you meant to replace it.

layout_asset_deleteA

Delete a layout_asset. IRREVERSIBLE. Refuses without force=true. Templates referencing the deleted file will render with empty content.

layouts_pullA

Fetch every layout + component from /layouts, plus the editable layout_assets (CSS/JS), and write them under target_dir: .tpl files to layouts/ and components/, assets to stylesheets/ javascripts/ assets/. Binary assets are skipped (no text to round-trip — use layout_asset_upload for those). Builds manifest.json mapping each local path to {id, type, updated_at}. REFUSES to overwrite an existing tree that already contains .tpl files — pick a fresh location or clear it first. Empty/non-tpl content in target_dir is fine (e.g. README.md, .gitignore are preserved).

layouts_pushA

Read manifest.json + tracked files from target_dir and PUT each to the endpoint its manifest type maps to: type=layout → /layouts/{id} {body}, type=asset (legacy: layout_asset) → /layout_assets/{id} {data}. Optional files=["javascripts/app.js", ...] filter pushes only the named relative paths; files=null (or omitted) pushes every manifest entry. THE BYTE-EXACT WAY to deploy a .js/.css asset: content is read from disk here, whereas a string passed to layout_asset_update crosses a JSON boundary that decodes literal \uXXXX escapes in the source (issue #138). A PUT that returns 200 without persisting is reported as a failure, not a ✓. Returns per-file success/failure breakdown; missing files and PUT errors are captured per-entry and do not abort the remaining pushes. Recommended pre-flight: site_snapshot for full backup before a mass push.

voog_list_my_sitesA

Probe GET /admin/api/me/sites to discover account metadata for a Voog token. Returns [{name, primary_domain, feature_flags}]. R6 NOTE: Voog API tokens are site-scoped, so this tool returns metadata for the ONE site the token belongs to — the array is always length 1. To enumerate multiple sites, the operator must supply each token separately. Token sourcing: prefer token_env= (env var name; secret stays in the environment). token= is a fallback for ad-hoc use BUT the token will appear in transcripts and host logs — avoid in production. host defaults to 'www.voog.com' (the canonical admin endpoint); override for tenants on private domains. Read-only.

media_set_getA

Get a media_set (gallery) by id (GET /media_sets/{id}). Returns a curated view: media_set id, title, kind, and the ordered assets array with each asset's id, position, title, filename, and type. Read-only. Use this to discover asset ids and current titles before calling media_set_update_asset_titles.

media_set_update_asset_titlesA

Safely edit one or more asset titles in a media_set (gallery). Pass titles as an object mapping asset id -> new title, e.g. {"24898880": "New alt text"}.

WHY THIS TOOL: PUT /media_sets/{id} is replace-not-merge — any asset omitted from the request body is unlinked from the gallery (same destructive semantics as product variants). Hand-rolling a partial-assets PUT to change one title silently drops every other image (hit live 2026-05-20). This tool does the GET-then-PUT-full-array dance for you: it reads the current media_set, applies only the requested title changes, and PUTs the complete asset array back — preserving every other asset's id, order, title, and link settings.

Every key in titles must be an asset id already present in the media_set; an unknown id is rejected (no silent no-op). Titles may be empty strings (Voog allows clearing a title). Idempotent — re-running with the same titles is a no-op.

media_set_set_assetsA

Set a media_set's FULL asset list in one call — for filling, reordering or pruning an EXISTING gallery (issue #140 item 5; media_set_update_asset_titles only edits titles of what is already there).

asset_ids is the gallery's new content IN ORDER (an explicit 1-based position is sent per asset — array order alone is NOT enough, Voog re-derives it and can duplicate positions). A reorder does not always take on the first PUT, so the tool reads the gallery back and retries; if the order still has not applied it says so instead of reporting success. Any asset currently in the set but absent from the list is UNLINKED (the asset itself survives in the library; only its membership ends). Because that is easy to do by accident, any call that drops a current asset requires force=true — including a same-length swap. Reordering and adding need no force.

Titles and per-asset link settings are carried over for assets that stay; pass titles to set them for new ones. Upload files first with asset_upload to get ids.

The media_set must already exist: POST /media_sets returns 500, and a freshly created gallery content area has no media_set until Voog makes one. Find the id on the content area's gallery field (NOT media_set) via voog://{site}/articles/{id}/contents, then media_set_get.

language_createA

Add a new language to the Voog site (POST /languages). Required: code (ISO 639-1 two-letter), title. Optional: region (ISO 3166-1 alpha-2), site_title, site_header, default_language, published, content_origin_id (duplicate content from another language). Body is FLAT — no envelope wrapper.

language_deleteA

Remove a language from the site (DELETE /languages/{id}). IRREVERSIBLE — Voog deletes the language and unbinds associated content. Requires force=true; without it the call is rejected. Run site_snapshot first if uncertain.

languages_listA

List all languages on the Voog site (id, code, title, default_language, published, position). Use the returned ids for page_create.language_id / article fields. Read-only.

nodes_listC

List all page nodes (id, title, parent_id, position). Each node represents a language-agnostic page identity; its parallel translations are pages sharing the same node.id. Read-only.

node_getA

Get a single node by id, with its full pages array — one entry per language. Use this when preparing a parallel translation: read the node id from one page, then pass node_id to page_create with the second-language details.

node_updateA

Update a node's title (PUT /nodes/{id}). Per Voog docs, only title is documented as updatable. Body is FLAT — no envelope wrapper. For tree restructuring use node_move (parent + position) or node_relocate (positional placement).

node_moveA

Move/reorder a node within the page tree (PUT /nodes/{id}/move). Inputs travel as query-string params per Voog docs. Required: parent_id (current or new parent — pass current to just reorder). Optional: position (1-indexed, Voog default 1).

node_relocateA

Relocate a node to a precise position relative to a sibling, or to the first slot under a new parent (PUT /nodes/{id}/relocate). Body is FLAT. Supply EXACTLY ONE of: before (place this node before the given sibling id), after (place after sibling id), or parent_node_id (move to first position under new parent). Mutually exclusive — handler rejects multiple.

orders_listA

List ecommerce orders (GET /admin/api/ecommerce/v1/orders). Read-only. Optional filters: status (e.g. 'created', 'cancelled'), payment_status (e.g. 'paid', 'unpaid'), created_after (ISO8601), created_before (ISO8601). include_pii=false (default) strips customer email / name / address / phone / IP via whitelist. include_pii=true REQUIRES force=true alongside — passing include_pii=true without force=true is rejected (LLM-side PII-exfiltration gate; see SECURITY.md). MCP hosts surface force as a destructive-hint approval, which keeps prompt-injected include_pii=true calls behind operator consent.

order_getA

Get a single order by id (GET /admin/api/ecommerce/v1/orders/{id}). Read-only. include_pii=false (default) strips PII via whitelist. include_pii=true REQUIRES force=true alongside (LLM-side PII-exfiltration gate; see SECURITY.md).

pages_listA

List pages on the Voog site (id, path, title, hidden, layout name). All filters are optional; with no filters this returns every page. Read-only.

page_getA

Get full details of a single page by id (title, path, hidden, layout, language, parent, timestamps, public_url).

page_set_hiddenA

Bulk toggle the hidden flag on one or more pages. Reversible — set hidden=false to make pages visible again. Returns a per-id breakdown showing which ids succeeded and which failed.

page_set_layoutA

Reassign a page's layout. Reversible — call again with the original layout_id to revert.

page_deleteA

Delete a page. IRREVERSIBLE — Voog does not retain deleted pages. Requires force=true; without it the call is rejected to prevent accidental deletion. Run pages_snapshot or site_snapshot first if the page might be needed later.

page_createA

Create a new page. Required: title, slug, language_id. Optional: parent_id (page id, NOT node_id) for subpages, node_id for parallel-translation pages of an existing page in another language, layout_id, content_type ('page'|'link'|'blog'|'product'|...), hidden, image_id, description, keywords, data (custom dict). Multilingual: pass node_id of the first-language page instead of parent_id when creating its translation in another language. Voog binds them as parallels (admin Translate UI works correctly). parent_id and node_id are mutually exclusive.

page_updateA

Update arbitrary fields on a page. At least one of title, slug, layout_id, image_id, content_type, parent_id, description, keywords, data must be supplied. For just hidden / layout id, prefer the dedicated page_set_hidden / page_set_layout — they're more explicit in tool listings.

data field is sent via PATCH (merge semantics) — only the keys you pass are touched. To delete a key, use page_delete_data. Calls without data route via PUT (full-field replace) as before.

page_set_dataA

Set a single page.data. value (PUT /pages/{id}/data/{key}). To delete a key use page_delete_data. Keys starting with 'internal_' are server-protected and rejected client-side.

page_delete_dataA

Delete a single page.data. (DELETE /pages/{id}/data/{key}). IRREVERSIBLE — the key is removed permanently. Requires force=true; without it the call is rejected. Keys starting with 'internal_' are server-protected and rejected client-side.

page_duplicateA

POST /pages/{id}/duplicate — create a copy of the page (including its content). The new page is hidden by default per Voog convention.

products_listA

List all ecommerce products on the Voog site (simplified: id, name, slug, sku, status, in_stock, on_sale, price, effective_price, stock, reserved_quantity, uses_variants, variants_count, translations, created_at, updated_at). Read-only. Same shape as the voog://products resource — consistent across the tools and resources surfaces. For per-variant stock on a variant-bearing product, follow up with product_get. Pass category_id to filter to products in that category (maps to q.category.id.$eq). Price fields (price, sale_price, effective_price) are net or gross depending on settings.price_entry_mode. Call ecommerce_settings_get to determine the mode. For customer-facing pricing always use effective_price and verify the mode first.

product_getA

Get full product details by id, including the per-variant variants array (with stock, reserved_quantity, variant_attributes_text), variant_types definitions, and translations (?include=variants,variant_types,translations). Read-only. Price fields (price, sale_price, effective_price) are net or gross depending on settings.price_entry_mode. Call ecommerce_settings_get to determine the mode. For customer-facing pricing always use effective_price and verify the mode first.

product_updateA

Update a product. Three argument shapes (combinable):

  • attributes: flat object of root-level product fields (status, price, sale_price, sku, stock, description, category_ids, image_id, asset_ids, physical_properties, uses_variants, variant_types, variants, variant_attributes). Note: asset_ids accepted; on PUT it's translated to the assets:[{id}] envelope Voog requires (sending raw asset_ids on PUT silently keeps only the hero image). variants without variant_attributes wipes ALL variants (even ones with id); pass both together, or set force=true to acknowledge.

  • translations: nested {field: {lang: value}} for translatable fields (name, slug, description). Each field-language pair must be non-empty. Cannot overlap with attributes (e.g. attributes.description + translations.description in the same call is rejected).

  • fields (legacy v1.1 shape): flat 'name-et', 'slug-en' keys — auto-routed to translations. Kept for back-compat. At least one of attributes/translations/fields must be non-empty. Validates status enum {'draft', 'live'} and rejects unknown attribute keys (catches typos before they round-trip to a 422). Reversible by calling with previous values; idempotent (same input twice = same end state). Price fields (price, sale_price, effective_price) are net or gross depending on settings.price_entry_mode. Call ecommerce_settings_get to determine the mode. For customer-facing pricing always use effective_price and verify the mode first.

product_createA

Create a new product (POST /products on ecommerce v1). Required: name, slug, price (Voog rejects POST without these). Three argument shapes (combinable):

  • attributes: flat object of root-level product fields. Allowed keys: name, slug, price, sale_price, status, description, sku, stock, reserved_quantity, category_ids, image_id, asset_ids, physical_properties, uses_variants, variant_types. Note: POST uses asset_ids (list of int), unlike PUT which uses assets:[{id}].

  • translations: nested {field: {lang: value}} for translatable fields (name, slug, description). Each field-language pair must be non-empty.

  • fields (legacy v1.1 shape): flat 'name-et', 'slug-en' keys — auto-routed to translations. Validates status enum {'draft', 'live'} and rejects unknown attribute keys. The POST result includes the newly assigned product id. Price fields (price, sale_price, effective_price) are net or gross depending on settings.price_entry_mode. Call ecommerce_settings_get to determine the mode. For customer-facing pricing always use effective_price and verify the mode first.

product_deleteA

Delete a product (DELETE /admin/api/ecommerce/v1/products/{id}). IRREVERSIBLE — Voog does not retain deleted products. Requires force=true; without it the call is rejected to prevent accidental deletion. Run products_list or product_get first to confirm the id, and site_snapshot if the product might be needed later.

product_duplicateA

Duplicate a product (POST /admin/api/ecommerce/v1/products/{id}/duplicate). The new product inherits status='draft' per Voog default — call product_update(status='live') after editing if the duplicate should be public. Returns the new product's full payload; summary surfaces new_id and new title for easy chaining into product_update.

products_bulk_actionA

Apply the same actions to many products in one request (PUT /admin/api/ecommerce/v1/products). This is NOT per-row arbitrary updates — every product in target_ids receives every action in actions. For one-off varied edits use product_update.

Request shape:

  • actions: list of {target_field, action, value, source_field?}. Allowed action verbs: set, increase_by_fixed, decrease_by_fixed, increase_by_percent, decrease_by_percent, round, round_upwards, round_downwards, merge, remove.

  • target_ids: list of integer product ids, OR the literal string 'all' to apply to every product on the site.

Response: {counters: {processed, failed}, processed_ids, failed_ids}. Duplicate ids in target_ids are collapsed server-side. No empirical batch-size cap observed up to 1001 ids (Stella, 2026-05-27); send what you need.

product_set_imagesA

Replace a product's images. files is a list of absolute paths to local image files (jpg, jpeg, png, webp, gif). First file becomes the main image (image_id); rest are gallery images. Runs Voog's 3-step asset upload protocol per file (POST /assets → PUT upload_url → PUT confirm), then PUTs {image_id, assets:[{id:n}]} to /products/{id}. Refuses to replace existing images unless force=true. If any single upload fails, the product is NOT updated — successful uploads are surfaced in uploaded for manual re-linking.

Gallery ORDER is applied by re-reading it back and repeating the PUT: Voog lands the requested order only about half the time on the first write (200 either way). If the order still has not taken, the call comes back as an ERROR carrying order_verified: false and the stored_asset_ids Voog actually holds. In that case every image IS linked and only the sequence is wrong — do NOT re-run this tool to fix it, that re-uploads every file as a new asset. Re-send just the order via voog_ecommerce_api_call PUT /products/{id}.

voog_admin_api_readA

Read-only Admin API passthrough. Forward a GET request to https:///admin/api. Use this when no typed read tool covers the endpoint (forms, tickets, tags, media_sets, etc.). Read-only — MCP hosts may skip the destructive-action confirmation prompt.

voog_ecommerce_api_readA

Read-only Ecommerce v1 API passthrough. Forward a GET request to https:///admin/api/ecommerce/v1. Supports ?include=... and ?language_code=.... Read-only — MCP hosts may skip the destructive-action confirmation prompt.

voog_admin_api_callA

Generic Admin API WRITE passthrough. Forward an HTTP request to https:///admin/api using the configured site's API token. method ∈ {POST, PUT, PATCH, DELETE}; body is JSON-serialised on POST/PUT/PATCH. Use this when no typed tool covers the endpoint (orders, forms, tickets, elements, tags, media_sets, webhooks, etc.). Conservative annotations (destructiveHint=true) — Claude will confirm before calling.

For READS use voog_admin_api_read — this tool no longer accepts method='GET' (removed in v1.5).

⚠️ PUT /media_sets/{id} is replace-not-merge: the assets array you send REPLACES the gallery — any asset omitted is unlinked. To edit asset titles safely use the typed media_set_update_asset_titles tool (GET-then-PUT-full-array); only hand-roll a media_sets PUT when you have the COMPLETE asset list. Same foot-gun as product variants.

⚠️ Ordered assets arrays (/media_sets/{id}, ecommerce /products/{id}) are applied only PARTIALLY by roughly half of single PUTs — 200 either way. Write, read the order back, and repeat if it disagrees, or use the typed tools (media_set_set_assets, product_set_images) which do that.

voog_ecommerce_api_callA

Generic Ecommerce v1 API WRITE passthrough. Forward an HTTP request to https:///admin/api/ecommerce/v1. Same shape as voog_admin_api_call, different base URL. Supports ?include=... and ?language_code=... per Voog ecommerce conventions. Use for orders, carts, discounts, shipping_methods, gateways, cart_fields, cart_rules, delivery_provider_configs, templates, bulk product actions, products imports, etc.

For READS use voog_ecommerce_api_read — this tool no longer accepts method='GET' (removed in v1.5).

PUT gotchas (Voog ecommerce v1 quirks — typed tools handle these for you, passthrough does not):

  1. On PUT /products/{id}, asset references must use the {"assets": [{"id": N}, ...]} shape. Sending the POST-shape asset_ids: [N, ...] on PUT silently drops all but the hero image. Prefer product_set_images for image attachment; it handles the shape internally. The array ORDER is also applied only partially by about half of single PUTs (200 either way) — read asset_ids back and repeat the PUT until it matches.

  2. On PUT /products/{id}, the variants array is destructive: Voog deletes every variant not present in the array — even variants with a stable id. Always include variant_attributes alongside variants, or send the full existing variant list. Prefer product_update; it requires explicit force=true to bypass this guard.

  3. On PUT to endpoints that accept a data hash (e.g. /pages/{id}, /articles/{id}, /site), the data field REPLACES the entire hash — unspecified keys are dropped. Voog supports PATCH (merge semantics) on these routes; use method='PATCH' here, or prefer the per-key tools page_set_data / article_set_data / site_set_data which route through PATCH automatically (typed wrappers handle this as of v1.4).

redirects_listA

List all redirect rules on the Voog site (id, source, destination, redirect_type, active). Read-only.

redirect_addA

Add a redirect rule. source/destination are paths (e.g. /old → /new). redirect_type defaults to 301; allowed: 301, 302, 307, 410. Set regexp=true to treat source as a regex pattern. Set active=false to create the rule disabled. For 410 (Gone), destination is semantically meaningless — Voog still stores it but never redirects there; pass any value (e.g. source path).

redirect_updateA

Update an existing redirect rule. At least one of source, destination, redirect_type, active, regexp must be supplied. redirect_type ∈ {301, 302, 307, 410}. Reversible by calling again with previous values.

Voog's PUT /redirect_rules/{id} is full-replace — missing fields are coerced to defaults (e.g. active flips to True). The tool GETs the current rule, merges your updates, then PUTs the full envelope, so unspecified fields are preserved.

redirect_deleteA

Delete a redirect rule. Refuses without force=true. Reversible only by re-creating the rule via redirect_add.

voog_searchA

Full-text search across the site's published content (GET /admin/api/search). Returns hits across pages, articles, elements, and products. Use scope to narrow the search to one kind. Indexing is hourly and covers PUBLIC content only — fresh edits and draft pages will not appear here. For draft / freshly-edited discovery, use pages_list(filters=...), articles_list(...), or text_get(...) instead. If indexing is disabled on the tenant, this tool detects that via a sentinel query and explains rather than silently returning zero. Read-only.

shipping_methods_listA

List all shipping methods (GET /admin/api/ecommerce/v1/shipping_methods). Read-only. Response includes the full options[] nested list for parcel-machine carriers (Omniva, SmartPost, …) — expect multi-KB payloads per method.

gateways_listA

List all payment gateways (GET /admin/api/ecommerce/v1/gateways). Read-only. Each entry has code, name, enabled, enabled_methods[], all_payment_methods[], url, created_at, updated_at.

site_getA

Get the site singleton (title, code, data, languages, ...). Read-only.

site_updateA

Update site singleton. attributes: flat root-level fields. site.code is immutable once set — passing it raises an error. For per-key data, use site_set_data.

site_set_dataA

Set site.data. to a value (PUT /site/data/{key}). To delete a key use site_delete_data. 'internal_*' keys are server-protected and refused client-side.

site_delete_dataA

Delete site.data. (DELETE /site/data/{key}). IRREVERSIBLE — the key is removed from site.data permanently. Requires force=true; without it the call is rejected. 'internal_*' keys are server-protected and refused client-side.

pages_snapshotA

Backup all pages + per-page contents to JSON files in output_dir. Creates the directory (and parents) if needed; overwrites existing pages.json. Lighter than site_snapshot — use this when you only need page structure and contents.

site_snapshotA

Comprehensive read-only backup of every mutable Voog resource: pages, articles, elements, layouts, layout_assets, languages, redirect_rules, nodes, texts, content_partials, tags, forms, media_sets, assets, webhooks, site, me, products (with translations + variant_types), per-page contents, per-article details, per-product details, and rendered HTML samples for VoogStyle capture. By default REFUSES to overwrite an existing directory — pick a fresh location. Pass overwrite=true to write into an existing directory (automation/cron use case); files from a prior snapshot may persist alongside new files if the underlying Voog state has shrunk. REQUIRED pre-flight before any risky operation: layout rename, mass push, layout swap, VoogStyle template push, page_delete. Writes _meta.json manifest to output_dir documenting voog-mcp version, attempted/succeeded/skipped/failed endpoints, request_count, duration_seconds, and (if the snapshot aborted mid-run) aborted_reason. Restore tooling reads this to refuse partial snapshots.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription
ArticlesAll blog articles on the Voog site (simplified: id, title, path, public_url, published, published_at, updated_at, created_at, language_code, page_id — without bodies). Single article body (rendered HTML) at voog://{site}/articles/{id} as text/html.
LayoutsAll layouts on the Voog site (simplified: id, title, component, content_type, updated_at — without bodies). Single layout body (raw .tpl source) at voog://{site}/layouts/{id} as text/plain.
PagesAll pages on the Voog site (simplified: id, path, title, hidden, layout, content_type, language, public_url). Per-page details available at voog://{site}/pages/{id}, page contents at voog://{site}/pages/{id}/contents.
ProductsAll ecommerce products on the Voog site (simplified: id, name, slug, sku, status, in_stock, on_sale, price, effective_price, stock, reserved_quantity, uses_variants, variants_count, translations, created_at, updated_at). Full product details with per-variant stock at voog://{site}/products/{id}.
Redirect rulesAll redirect rules on the Voog site (id, source, destination, redirect_type, active).

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/runnel/voog-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server