servers / com-duck-hub-menu

com.duck-hub/menu MCP server

communitystreamable_httpremotedestructive capablehealthy

Hosted MCP server to manage a restaurant menu from AI agents - 39 tools over the DuckHub API.


01Tools · 39
ToolRiskSide effectsApproval
get_reference
Call this FIRST. Returns everything needed to use the other tools correctly: allowed enum values (units, badge colors, modifier types, card styles, currencies, usable languages), THIS venue’s plan limits and current usage, rate-limit budgets, order-status transitions, request caps, and the index of all API resources. Also documents conventions: all money amounts are integers in minor currency units.
writetrueunknown
get_menu
Read the venue’s current (draft) menu. view:"structure" (default) returns a tiny name↔ref map of categories/products — the cheapest overview; use it to learn existing refs before mutating. view:"full" returns every product with all fields (heavy — only when field-level detail is needed). Pass locale to resolve all translatable fields into that language. Rows are keyed by `ref` (externalId if set, else id).
writetrueunknown
build_menu
THE bulk tool: create or update the whole menu in one call — categories, ingredients and products (with composition, modifier groups and inline translations), all keyed by YOUR externalId and idempotent (safe to retry; re-running updates in place). For large or first-time payloads call with dryRun:true first — full validation and plan-limit checking with a create/update/skip preview and ZERO writes. CAUTION (replace semantics): when a product includes ingredientExternalIds, modifierGroups or modifierGroupExternalId, that array is treated as the FULL set and REPLACES what exists — anything unlisted is detached. For single-field edits of one product prefer the set_product tool (which never touches those arrays). Changes affect the DRAFT menu only — nothing is guest-visible until publish. Money is in minor units (12.50 → 1250).
writetrueunknown
get_settings
The complete curated settings view: venue info, working hours, SEO + social links, design, languages, ordering/delivery config, notification wiring — plus read-only context (publicMenuUrl = the live guest menu URL, lastPublishedAt). The result includes an `etag` field; the update_* settings tools handle concurrency automatically, so you normally never need it.
readfalseunknown
update_ordering_config
Online-ordering configuration — REQUIRES a plan with the delivery feature (get_reference → planLimits.features.hasDelivery); on other plans every field here fails with 403 DELIVERY_NOT_AVAILABLE. Toggles: deliveryEnabled, pickupEnabled, dineInOrderingEnabled. Money fields are integers in MINOR currency units (12.50 → 1250): minimumOrderAmount, deliveryFee, freeDeliveryThreshold. pickupDiscount is a PERCENT (0-100). Times are minutes. deliveryEmail receives order notifications. (Ordering HOURS live in update_working_hours → orderSchedule.) Returns a COMPACT delta — only this tool’s own fields plus the new etag — not the full settings object; read complete state via get_settings.
writetrueunknown
delete_banner
Deletes a menu banner by ref (externalId or id), including its uploaded image. To just hide a banner temporarily, prefer set_banner with isActive:false. Cannot be undone.
destructivetruetrue
list_tables
The venue’s tables keyed by ref — each row carries its guest QR-menu URL (qrUrl). Paginated (limit max 100).
readfalseunknown
set_table
Upsert a table: pass ref to update (name, isActive, floor-plan positionX/positionY, capacity), omit to create (name required, max 50; set externalId for stable addressing). The response includes the table’s guest QR-menu URL (qrUrl). Plan-limited (TABLE_LIMIT_REACHED — check get_reference limits). Note on create with extras (isActive/position/capacity): two internal steps (create, then patch extras) — if the second fails the table EXISTS without them; re-run with ref to finish.
writetrueunknown
delete_table
Deletes a table by ref (externalId or id) — its printed QR code stops resolving immediately. To take a table out of service temporarily, prefer set_table with isActive:false. Cannot be undone.
destructivetruetrue
list_orders
Guest orders (paid plans only — 403 PAID_PLAN_REQUIRED on the free plan). Filters: status, since/until/updatedSince (ISO 8601). Paginated (limit max 100). Poll updatedSince for changes.
readfalseunknown
get_order
Full single-order read by order id (paid plans only — 403 PAID_PLAN_REQUIRED on the free plan).
writetrueunknown
update_order_status
Advances an order through its lifecycle (paid plans only). Allowed transitions are in get_reference → orderStatusTransitions (e.g. new → confirmed|cancelled); an invalid transition is rejected. Statuses: new, confirmed, preparing, delivering, completed, cancelled.
writetrueunknown
get_audit_log
What this API key changed and when — every v1 write is recorded (action, entity, timestamp). Filter by exact action (e.g. "menu.sync", "settings.update"); paginated (limit max 100, default 20). Useful to review your own session or debug unexpected state.
writetrueunknown
list_products
Filtered, paginated product list. Default view:"summary" returns sparse rows (ref, name, price, flags — token-cheap); view:"full" returns complete product objects. Filters: category (ref), search (name), withoutDescription:true (only products missing a description — useful for content gap-filling). Pagination: page (1-based) + limit (max 50, default 15); the response envelope carries total/totalPages. Rows are keyed by `ref` (externalId if set, else id) — use refs with get_product/set_product/delete_product.
writetrueunknown
get_product
Full single-product read by ref (externalId or id). The result includes an `etag` field — pass it as ifMatch to set_product so a concurrent change is detected (412) instead of silently overwritten. Pass locale to resolve translated fields.
readfalseunknown
list_categories
Lightweight category index with live product counts, keyed by ref. Pass locale to resolve translated names.
readfalseunknown
set_product
Partial update of ONE product by ref — the safe alternative to build_menu for single-field edits. TRI-STATE semantics on every field: a field you OMIT keeps its current value; explicit null CLEARS it (nullable fields only); a value SETS it. Never resend fields you do not want to change. Composition and modifier groups are deliberately NOT editable here (their full-set replace semantics belong to build_menu). categoryRef: null detaches the product from its category; an unknown categoryRef is a hard 404. Concurrency: pass ifMatch with the etag from get_product — on 412 STALE_RESOURCE re-read and retry with the fresh etag. The result includes the NEW etag for chained edits. Money in minor units (12.50 → 1250).
writetrueunknown
delete_product
Soft-deletes ONE product by ref. Unlike cleanup_menu (which only HIDES products from the menu), this frees plan-limit capacity — deleted products no longer count toward maxProducts. Cannot be undone via the API.
destructivetruetrue
list_publications
Publication history (rollback buffer, newest first, ~5 retained snapshots) with ids and timestamps. Use before rollback_publish to pick a snapshot.
readfalseunknown
publish_menu
Publishes the current DRAFT menu to the public guest-facing menu — takes effect immediately. All build_menu/set_product changes stay invisible to guests until this is called. Each publish keeps a snapshot in the rollback history (rollback_publish undoes a bad publish).
writetrueunknown
rollback_publish
Restores a retained snapshot as the LIVE public menu (undo for a bad publish) — replaces what guests currently see, immediately. Omit publicationId to roll back to the previous snapshot; otherwise pick an id from list_publications.
writetrueunknown
cleanup_menu
⚠️ DANGEROUS BULK OPERATION with KEEP-LIST semantics: each array lists the externalIds to KEEP — everything else of that type is affected (products get HIDDEN from the menu [menuVisible:false, plan capacity NOT freed — use delete_product for that], categories and ingredients get DELETED). An omitted array leaves that type untouched; an EMPTY array wipes ALL items of that type and requires force:true. ALWAYS call with dryRun:true first — it returns counts plus a preview of exactly what WOULD be hidden/deleted, with zero writes. Typical use: after a full build_menu, remove leftovers not present in your source data.
destructivetruetrue
update_venue_info
Core venue identity: name, address, phone, Google-Maps link, Wi-Fi password, extra info text, country (ISO alpha-2, uppercase), cuisine type, IANA timezone, default currency. Valid currency codes are in get_reference → currencies. NOTE: renaming (name) changes the DISPLAYED venue name only — the public menu URL slug (yourvenue.duck-hub.com) is fixed at venue creation and never changes (protects printed QR codes and links). Omit = keep current value, null = clear, value = set. — null works only on country and cuisineType; leave other fields out to keep them. Returns a COMPACT delta — only this tool’s own fields plus the new etag — not the full settings object; read complete state via get_settings.
writetrueunknown
update_working_hours
schedule = the venue’s opening hours shown on the public menu (full 7-day array replaces the current one). orderSchedule = the separate hours during which ONLINE ORDERS are accepted — it is delivery-plan-gated: on a plan without the delivery feature setting it fails with 403 DELIVERY_NOT_AVAILABLE (plain schedule always works). Returns a COMPACT delta — only this tool’s own fields plus the new etag — not the full settings object; read complete state via get_settings.
writetrueunknown
update_menu_design
Public-menu look: theme (LIGHT/DARK), brandColor (#RRGGBB), menuFontFamily (font id — lowercase letters/digits/hyphens; null = default font), menuCardStyle (venue-wide card design — valid values in get_reference → cardStyles; per-category overrides live on categories via build_menu cardStyle). Returns a COMPACT delta — only this tool’s own fields plus the new etag — not the full settings object; read complete state via get_settings.
writetrueunknown
update_languages
defaultLanguage = the canonical content language; enabledLanguages = ALL locales the public menu offers (min 1, replaces the current list — include the default). Valid codes are in get_reference → languages. Enable a locale HERE first, then fill content via build_menu inline translations (a locale that is not enabled fails with LOCALE_NOT_ENABLED). Returns a COMPACT delta — only this tool’s own fields plus the new etag — not the full settings object; read complete state via get_settings.
writetrueunknown
update_seo_and_links
Search/social presence of the public menu page: seoTitle (max 60), seoDescription (max 160), googlePlaceId, and social/review links (Instagram, Telegram, TikTok, Twitter/X, TripAdvisor, Google Reviews). Omit = keep current value, null = clear, value = set. — null works on seoTitle, seoDescription and googlePlaceId. Returns a COMPACT delta — only this tool’s own fields plus the new etag — not the full settings object; read complete state via get_settings.
writetrueunknown
update_notifications
Staff-notification wiring: notify toggles (waiter call, hookah, bill request, booking, order) and the Telegram chat ids that receive them (telegramChatId for service actions, orderTelegramChatId for orders; null disconnects a chat). NOTE: every change here is recorded in the venue audit log — agents must not silently redirect staff notifications. Returns a COMPACT delta — only this tool’s own fields plus the new etag — not the full settings object; read complete state via get_settings.
writetrueunknown
set_image
Attach an image: target "product" or "banner" (ref required) or "logo" (venue logo, no ref). Source: EXACTLY ONE of sourceUrl (public https URL, server fetches it, ~15 MB cap) or base64 (inline bytes). Aspect rules: product and logo images must be SQUARE 1:1 — pass crop:"square" to have the server center-crop automatically; banner images must be 16:9 (no crop option). The server re-encodes/optimizes everything (WebP). preoptimized:true (product/banner only) uses the softer fast-lane rate bucket but REQUIRES the bytes to already be a target-size WebP — otherwise 400 PREOPTIMIZED_INVALID; when unsure, omit it.
writetrueunknown
set_translations
Cross-entity BULK translation upsert — up to 50 items in ONE call (one rate-limit hit) with per-item results (one bad item fails alone). Each item: model + ref (externalId or id) + translations. Translatable fields per model: product = name/description/badgeLabel; category/ingredient/modifierGroup = name; banner = title/description. translations accepts BOTH equivalent forms: the entries ARRAY [{"locale":"uk","name":"Піца"}] (canonical wire form) or the locale-keyed OBJECT {"uk":{"name":"Піца"}} — the SAME form build_menu inline translations use; the object form is normalized before sending. MERGE semantics per field: omitted = keep, empty string "" = delete that translation, non-empty = set. The locale must be enabled for the venue first (update_languages) — otherwise LOCALE_NOT_ENABLED. TIP: when building a menu from scratch, prefer inline `translations` on build_menu items; use this tool for after-the-fact localization passes.
destructivetruetrue
get_translations
statsOnly:true → multilingual completeness in one call: per locale × model counts of translated/untranslated rows (no other params needed) — start here to find gaps. Otherwise a batch translation read: locale AND model are REQUIRED; filter narrows to untranslated/translated/stale rows; fields limits which fields return; refs scopes to specific entities. Rows are keyed by ref. Paginated (limit max 50).
readfalseunknown
list_modifier_groups
The venue’s reusable modifier-group library (restaurant-scoped option groups), keyed by ref, including usedOnProducts (which products each group is attached to). Attach groups to products via build_menu modifierGroupExternalId[].
readfalseunknown
set_modifier_group
Upsert a REUSABLE modifier group (edit once, applies on every product it is attached to). Pass ref to UPDATE an existing group (only the provided fields change; options — when provided — REPLACE the whole option set); omit ref to CREATE (name + type required; set externalId so you can address it later). type: single_choice | add_ingredients | remove_ingredients — legacy multiple_choice is NOT accepted here (build_menu’s INLINE modifierGroups still accept it for backward compatibility and normalize it to add_ingredients; this tool takes canonical types only). Options reference ingredients by ingredientRef (externalId or id); priceAdjustment is in minor units and may be negative. Attach to products via build_menu modifierGroupExternalId[]; translate via set_translations.
writetrueunknown
delete_modifier_group
Deletes a reusable modifier group by ref. If the group is attached to any product the call fails with 409 MODIFIER_GROUP_IN_USE — pass force:true to detach it from every product and delete.
destructivetruetrue
list_promotions
All promotions with their items (each item: productId, salePriceMinor, product summary). Paginated (limit max 100). Keyed by ref (externalId if set, else id).
writetrueunknown
set_promotion
Upsert a promotion in ONE call — pass ref to update, omit to create (name required; set externalId for stable addressing). Optional items[] is the FULL desired item set: the server diffs it against the current items and adds/updates/removes accordingly (item identity = the product; salePriceMinor in minor units). Products not listed are REMOVED from the promotion — omit items entirely to leave them untouched. isActive toggles the promotion AND syncs the products’ live sale prices. schedule = optional weekly activity hours (day/isOpen/HH:MM). NOT atomic: the result reports added/updated/removed/unchanged/errors per item — on partial failure the promotion and successful items ARE saved; re-run set_promotion with the same ref/externalId and the full desired items[] to converge (the diff is idempotent).
writetrueunknown
delete_promotion
Deletes a promotion by ref (externalId or id). Its items are removed with it and the affected products’ live sale prices are un-synced back to normal. Cannot be undone.
destructivetruetrue
list_banners
Menu banners keyed by ref. Pass locale to localize title/description. Paginated (limit max 100).
readfalseunknown
set_banner
Upsert a menu banner: pass ref to update, omit to create (title required, max 60 chars; description max 2000; set externalId for stable addressing). linkedProductRef links the banner to a product (null clears the link). isActive shows/hides it. Upload the banner image (16:9) separately via set_image target:"banner". Plan-limited (BANNER_LIMIT_REACHED — check get_reference limits). Note on create with isActive: two internal steps (create, then set isActive) — if the second fails the banner EXISTS without it; re-run with ref (or the same externalId → the conflict hint points you at the existing banner).
writetrueunknown

02Install & source
https://mcp.duck-hub.com/mcp
remote_url

03Access granted
Manage e-commerce · destructiveMaps & location · writeSend chat messages · write

The access this server can exercise, inferred from its verified tools — not a declared OAuth scope.


05Provenance & freshness
sourcesOfficial MCP Registry [p1]
last_checked2026-07-08 08:51Z
next_check2026-07-09 13:38Z
cadenceevery 29h
verifiedtools_list:passed handshake:passed metadata:passed
index_statusindex9 unique facts >= 5

06Badge

Add the “as seen on MCPExplorer” badge to your README. com.duck-hub/menu MCP — as seen on mcpexplorer.com

[![com.duck-hub/menu MCP — as seen on mcpexplorer.com](https://mcpexplorer.com/badge/com-duck-hub-menu.svg)](https://mcpexplorer.com/servers/com-duck-hub-menu)

Next step

This is one server. A loadout combines the right servers, governance, and proven plays for a whole job — assembled deliberately, not tool-dumped.

Explore loadouts →