{"app":{"handle":"product-maestro","name":"Product Maestro"},"available":true,"endpoint":"https:\/\/api.sappo.xco.agency\/product-maestro\/mcp","protocolVersions":["2025-11-25","2025-06-18","2025-03-26","2024-11-05"],"server":{"name":"Product Maestro","version":"1.0.0"},"instructions":"Product Maestro MCP server for accessing product schedule data from Shopify stores.\n\nThe store is identified automatically from your API token.\n\nAvailable tools:\n- `list_schedules`: List product schedules with optional status\/archived filters and pagination\n- `get_schedule`: Get a specific schedule by ID with its actions\n- `list_actions`: List schedule actions with optional status\/type filters and pagination\n- `create_draft_schedule`: Create a draft schedule from structured actions (needs the schedules:write scope). Drafts never run on their own\n- `execute_schedule`: Always refuses. Schedules are never run over the API\n\nDrafting and running are deliberately separate. A draft runs only after the merchant reviews it in Product Maestro and clicks Save, so tell the merchant to do that rather than looking for a way to run it.","auth":{"tokenPrefix":"mak_","header":"Authorization","scheme":"Bearer","whereToGet":"Open the app in your Shopify admin, go to Settings \u2192 API Access, and create a token. The full token is shown once, at creation time; afterwards only its first 12 characters are stored in readable form. If that card is locked, API access is not included in your current plan and the app will not create a token until the plan changes. At most three tokens can be active at once, so revoke one you no longer use before creating another.","plan":{"required":false,"plans":[]}},"clientConfig":{"claudeDesktop":{"mcpServers":{"sappo-product-maestro":{"type":"http","url":"https:\/\/api.sappo.xco.agency\/product-maestro\/mcp","headers":{"Authorization":"Bearer mak_your_token_here"}}}},"cli":"claude mcp add --transport http sappo-product-maestro https:\/\/api.sappo.xco.agency\/product-maestro\/mcp --header \"Authorization: Bearer mak_your_token_here\""},"tools":[{"name":"list_schedules","title":"List Schedules Tool","description":"List product schedules for a Shopify store. Returns paginated schedules with their associated actions.","inputSchema":{"properties":{"page":{"description":"Page number (default: 1)","type":"integer"},"page_size":{"description":"Items per page, max 100 (default: 25)","type":"integer"},"status":{"description":"Filter by status","type":"string"},"archived":{"description":"Filter by archived state","type":"boolean"}},"type":"object"},"examplePrompt":"Show me the schedules."},{"name":"get_schedule","title":"Get Schedule Tool","description":"Get a single product schedule by ID for a Shopify store, including its actions.","inputSchema":{"properties":{"id":{"description":"Schedule ID","type":"integer"}},"type":"object","required":["id"]},"examplePrompt":"Give me the details of a schedule."},{"name":"list_actions","title":"List Actions Tool","description":"List product schedule actions for a Shopify store using Product Maestro.","inputSchema":{"properties":{"page":{"description":"Page number (default: 1)","type":"integer"},"page_size":{"description":"Items per page, max 100 (default: 25)","type":"integer"},"status":{"description":"Filter by action status","enum":["SCHEDULED","PENDING","IN_PROGRESS","COMPLETED","FAILED","RECURRING"],"type":"string"},"type":{"description":"Filter by action type","type":"string"}},"type":"object"},"examplePrompt":"Show me the actions."},{"name":"create_draft_schedule","title":"Create Draft Schedule Tool","description":"Create a draft product schedule for the store. Drafts never run on their own: the merchant must open the draft in Product Maestro, review it, and click Save. Actions that share a selection land on one draft; a different selection gets its own draft.\n\nEach action needs actionType + targetType from the list below, a selectionType valid for that target (PRODUCT: PRODUCTS, COLLECTION or ADVANCED; COLLECTION: COLLECTION; VARIANTS: VARIANTS; PRICE_LIST: PRICE_LIST), parameters matching the shape shown, and scheduledAtLocal as the store's local wall-clock time (\"2026-10-03T09:00\", no offset). filterConditions are only allowed with selectionType ADVANCED.\n\nSupported actions (actionType (targets) \u2014 parameters):\n- ADD_TAG (PRODUCT) \u2014 parameters: { tags: string[] }\n- REMOVE_TAG (PRODUCT) \u2014 parameters: { tags: string[] }\n- PUBLISH (PRODUCT) \u2014 parameters: {} \u2014 no fields\n- DRAFT (PRODUCT) \u2014 parameters: {} \u2014 no fields\n- ARCHIVE (PRODUCT) \u2014 parameters: {} \u2014 no fields\n- UPDATE_TEMPLATE_SUFFIX (PRODUCT\/COLLECTION) \u2014 parameters: { templateSuffix: string }\n- JOIN_COLLECTION (PRODUCT) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- LEAVE_COLLECTION (PRODUCT) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- UPDATE_PRODUCT_METAFIELDS (PRODUCT\/COLLECTION) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- UNPUBLISH_FROM_CHANNEL (PRODUCT\/COLLECTION) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- PUBLISH_INTO_CHANNEL (PRODUCT\/COLLECTION) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- INCREASE_PRICE (PRODUCT) \u2014 parameters: { amount: number, amount_type: \"fixed\"|\"percentage\", round?: \"up\"|\"down\"|\"closest\" } \u2014 round only applies when amount_type is \"percentage\"\n- DECREASE_PRICE (PRODUCT) \u2014 parameters: { amount: number, amount_type: \"fixed\"|\"percentage\", round?: \"up\"|\"down\"|\"closest\" } \u2014 round only applies when amount_type is \"percentage\"; the reverse of this action is INCREASE_PRICE with the same amount\/amount_type\n- INCREASE_INVENTORY (PRODUCT) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- DECREASE_INVENTORY (PRODUCT) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- SET_INVENTORY (PRODUCT) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- SET_PRICE (PRODUCT) \u2014 parameters: { amount: number }\n- SET_PRICE_DECIMAL (PRODUCT) \u2014 parameters: { amount: number }\n- UPDATE_TITLE (PRODUCT\/COLLECTION) \u2014 parameters: { title: string }\n- UPDATE_COLLECTION_SORT (COLLECTION) \u2014 parameters: { collectionSort: string }\n- ROUND_PRICE (PRODUCT) \u2014 parameters: { round: \"up\"|\"down\"|\"closest\" }\n- UPDATE_BODY_HTML (PRODUCT\/COLLECTION) \u2014 parameters: { bodyHtml: string } \u2014 an empty string is a valid instruction: it clears the description\n- DELETE_PRODUCT (PRODUCT) \u2014 parameters: {} \u2014 no fields; only propose this if the merchant explicitly asked to delete, never as a guess\n- DELETE_COLLECTION (COLLECTION) \u2014 parameters: {} \u2014 no fields; only propose this if the merchant explicitly asked to delete, never as a guess\n- PRODUCT_SET (PRODUCT) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- REORDER_COLLECTION_PRODUCTS (COLLECTION) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- PRICE_LIST_UPDATE (PRICE_LIST) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- PRICE_LIST_FIXED_PRICES_ADD (PRICE_LIST) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- PRICE_LIST_FIXED_PRICES_BY_PRODUCT_UPDATE (PRICE_LIST) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- PRICE_LIST_FIXED_PRICES_DELETE (PRICE_LIST) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- PRICE_LIST_FIXED_PRICES_UPDATE (PRICE_LIST) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- UPDATE_FEATURED_IMAGE (PRODUCT\/COLLECTION) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- UPDATE_VARIANT (VARIANTS) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- UNLIST (PRODUCT) \u2014 parameters: {} \u2014 no fields\n- UPDATE_COLLECTION_SEO (COLLECTION) \u2014 parameters: { title?: string, description?: string } \u2014 at least one is required\n- UPDATE_PRODUCT_SEO (PRODUCT) \u2014 parameters: { title?: string, description?: string } \u2014 at least one is required\n- PRODUCT_MEDIA_UPDATE (PRODUCT) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- ADD_VARIANT (VARIANTS) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- REMOVE_VARIANT (VARIANTS) \u2014 parameters: {} \u2014 shape not documented yet, ask for nothing beyond selection\/date\n- PUBLISH_INTO_MARKET (PRODUCT) \u2014 parameters: { markets: array } \u2014 one or more market identifiers named by the merchant\n- UNPUBLISH_FROM_MARKET (PRODUCT) \u2014 parameters: { markets: array } \u2014 one or more market identifiers named by the merchant","inputSchema":{"properties":{"summary":{"description":"One line describing the intended change. The merchant sees it on the draft when reviewing.","type":"string"},"actions":{"minItems":1,"maxItems":10,"items":{"properties":{"actionType":{"type":"string"},"targetType":{"enum":["PRODUCT","COLLECTION","PRICE_LIST","VARIANTS"],"type":"string"},"selectionType":{"enum":["PRODUCTS","COLLECTION","VARIANTS","PRICE_LIST","ADVANCED"],"type":"string"},"selectionQuery":{"description":"What the action applies to, in words, e.g. \"Winter Coats collection\". The merchant picks the exact items when reviewing.","type":"string"},"filterConditions":{"items":{"properties":{"type":{"enum":["not_sold_days","total_orders"],"type":"string"},"operator":{"enum":["gt","gte","lt","lte","eq"],"type":"string"},"value":{"type":"number"}},"type":"object","required":["type","operator","value"]},"type":"array"},"parameters":{"description":"The parameters shape for this actionType, from the tool description.","type":"object"},"scheduledAtLocal":{"description":"Store-local wall-clock time, no offset, e.g. \"2026-10-03T09:00\".","type":"string"},"repeat":{"enum":["NONE","HOURLY","DAILY","WEEKLY","BI_WEEKLY","MONTHLY"],"type":"string"}},"type":"object","required":["actionType","targetType","selectionType","scheduledAtLocal"]},"type":"array"}},"type":"object","required":["summary","actions"]},"examplePrompt":"Use create_draft_schedule to tell me about the draft schedule."},{"name":"execute_schedule","title":"Execute Schedule Tool","description":"Product Maestro does not run schedules over the API. This always refuses, and explains that the merchant must review the draft in the app and click Save; it then runs at its scheduled time.","inputSchema":{"properties":{"id":{"description":"Schedule ID","type":"integer"}},"type":"object"},"examplePrompt":"Use execute_schedule to tell me about the schedule."}],"errors":[{"status":401,"code":"MISSING_TOKEN","message":"Authorization header with Bearer token is required.","whatToDo":"Send an `Authorization: Bearer mak_\u2026` header. This code also appears when the header is present but does not start with `Bearer ` \u2014 a bare token or a `Token \u2026` prefix is not accepted."},{"status":401,"code":"INVALID_TOKEN","message":"Token is invalid, expired, or revoked.","whatToDo":"Check the token was copied in full, is still listed as active in Settings \u2192 API Access, and has not passed its expiry date. Create a new token if in doubt. The same code is returned when the store is suspended."},{"status":403,"code":"PLAN_REQUIRED","message":"API access is not included in the current plan.","whatToDo":"The token is valid, but this app sells API access on named plans only and this store is not on one of them. The plans that carry it are listed under **Authentication** above; change the plan in the app and the same token starts working \u2014 there is no need to create a new one."},{"status":404,"code":"APP_NOT_FOUND","message":"App not found.","whatToDo":"The app handle in the path is not one Sappo serves. Use the handle this documentation was generated for, and check for a typo or a stray path segment."},{"status":429,"code":"RATE_LIMITED","message":"Too many requests. Please slow down.","whatToDo":"Wait the number of seconds in the `Retry-After` header before retrying, and keep each client under 60 requests per 60 seconds. Fetch more records per request instead of making more requests."},{"status":503,"code":"API_NOT_AVAILABLE","message":"The API is not available for this app yet.","whatToDo":"The app is served but its store of API tokens has not been created yet, so no token can be checked. Nothing is wrong with your request: retry later, and tell support if it lasts."}],"throttle":{"requests":60,"windowSeconds":60,"scope":"client address","message":"Too Many Attempts.","headers":["X-RateLimit-Limit","X-RateLimit-Remaining","X-RateLimit-Reset","Retry-After"]}}