{"schemaStatus":"published","endpoints":[{"path":"/shop-api/mcp/","methods":["GET","POST"],"description":"POST: process a customer's authenticated MCP JSON-RPC requests. GET: this guide.","acceptsPost":true,"actions":[]},{"path":"/shop-api/authorize","methods":["GET","POST"],"description":"Customer sign-in and approval page (OAuth authorization endpoint).","acceptsPost":true,"actions":[]},{"path":"/shop-api/token","methods":["POST"],"description":"Exchange an authorization code or refresh token.","acceptsPost":true,"actions":[]},{"path":"/shop-api/.well-known/oauth-protected-resource/mcp","methods":["GET"],"description":"OAuth protected resource metadata of the MCP.","acceptsPost":false,"actions":[]},{"path":"/shop-api/.well-known/oauth-authorization-server","methods":["GET"],"description":"OAuth authorization server metadata (also at /shop-api/.well-known/openid-configuration).","acceptsPost":false,"actions":[]},{"path":"/shop-api/mcp/server-card.json","methods":["GET"],"description":"MCP server card (draft discovery format).","acceptsPost":false,"actions":[]}],"apiVersion":"1.4.43","version":2,"guide":{"description":"Connect an AI assistant to this website's shop on behalf of one of its customers.","title":"Shop MCP","sections":[{"title":"What this is","paragraphs":["The shop MCP lets an AI assistant act for ONE signed-in customer of THIS website's shop: find products at the prices that customer would really pay, work out the final price of an order, place it, book appointments, read their past orders, and repeat one. Customers are the website's members; their level and groups decide every product, category and price they can see.","Nothing a customer cannot do on the website can be done here. The assistant never chooses the member, the website or a price: the website is the one that owns this domain and the member is the one who signed in."]},{"links":[{"label":"Protected resource metadata","url":"https://www.birdofprey.co.nz/shop-api/.well-known/oauth-protected-resource/mcp"},{"label":"MCP server card (draft format)","url":"https://www.birdofprey.co.nz/shop-api/mcp/server-card.json"}],"title":"Connection","paragraphs":["Send JSON-RPC 2.0 messages by POST to https://www.birdofprey.co.nz/shop-api/mcp/ with Content-Type application/json and Authorization: Bearer access-token. The address is on the website's own domain; the website is whichever one owns the domain, so there is nothing else to configure.","Call initialize first, send notifications/initialized, then tools/list and tools/call. ping and notifications/cancelled are supported. Protocol version 2025-11-25. Every request is independent (no MCP session).","GET https://www.birdofprey.co.nz/shop-api/mcp/ returns this guide as JSON."]},{"links":[{"label":"Authorization server metadata","url":"https://www.birdofprey.co.nz/shop-api/.well-known/openid-configuration"}],"title":"OAuth discovery","paragraphs":["A request without a valid token gets HTTP 401 with WWW-Authenticate: Bearer resource_metadata=\"https://www.birdofprey.co.nz/shop-api/.well-known/oauth-protected-resource/mcp\". An MCP client follows that automatically; nothing needs configuring except the address of the MCP above.","1. GET https://www.birdofprey.co.nz/shop-api/.well-known/oauth-protected-resource/mcp (RFC 9728) names the resource, the authorization server and the scopes.","2. The authorization server is https://www.birdofprey.co.nz/shop-api, the same one the shop's REST API uses. Its metadata is at https://www.birdofprey.co.nz/.well-known/oauth-authorization-server/shop-api (RFC 8414) where the website's web server passes that path on, and always at https://www.birdofprey.co.nz/shop-api/.well-known/openid-configuration (the OpenID-style location that MCP clients try next) and https://www.birdofprey.co.nz/shop-api/.well-known/oauth-authorization-server.","3. The authorization endpoint is https://www.birdofprey.co.nz/shop-api/authorize and the token endpoint https://www.birdofprey.co.nz/shop-api/token.","Each website is its own authorization server: a token from one website is not accepted by another, nor by the CMS Tool's API."]},{"title":"Signing in (OAuth 2.1)","paragraphs":["Authorization code with PKCE (S256 only) for public clients. Clients identify themselves either with a registered client_id or with a client metadata document (the client_id is an https URL the server fetches); redirect URIs must match the client's.","Send the customer's browser to the authorize endpoint with response_type=code, client_id, redirect_uri, code_challenge, code_challenge_method=S256, state and scope. A customer who is already signed in to the website only sees what they are being asked to allow, and presses Allow; otherwise they sign in with their own login for the site (password and two-factor where they use it) on the same page. They can allow fewer scopes than were asked for.","Exchange the code at the token endpoint (grant_type=authorization_code, code, code_verifier, client_id, redirect_uri) for an access token and a refresh token. grant_type=refresh_token renews them; a refresh token works once.","A connection belongs to the customer and the application, not to their browser: signing out of the website does not end it. It ends when the customer or the shop revokes it, when the refresh token runs out (90 days since it was last used), or when the customer's account can no longer be used. Allowing the same application again replaces its connection.","Members at level 0 or below, and websites that are not live, cannot sign in. Repeated failed sign-ins lock the login for 15 minutes."]},{"title":"Scopes","paragraphs":["The customer approves each scope separately. A tool called without its scope is refused with JSON-RPC error -32003. submit_order is the only tool that commits the customer to buying."],"table":{"columns":["Scope","What the customer is told","Tools"],"rows":[["shop/browse","Look at the products, categories, currencies and booking times, with your prices","get_categories, search_products, get_product, get_currency, get_booking_availability, get_booking_end_times"],["shop/profile:read","See your saved details: contact details, addresses, interests and preferences","get_profile"],["shop/profile:write","Change your saved details (not your password or login email)","save_profile"],["shop/orders:read","See your past orders","get_orders"],["shop/orders:draft","Prepare orders and choose booking times for you (nothing is bought until you approve it)","create_order, repeat_order"],["shop/orders:submit","Place orders for you","submit_order"],["shop/orders:cancel","Cancel your orders where the shop allows it","cancel_order"],["shop/contact","Send messages to the business for you, from your own email address","contact_business"]]}},{"title":"Tools","paragraphs":["tools/list returns the full argument schema of each tool."],"table":{"columns":["Tool","Scope","Limit","Purpose"],"rows":[["get_categories","shop/browse","30 calls/minute; 4 concurrent","List the product categories this customer may see, as a tree."],["search_products","shop/browse","30 calls/minute; 4 concurrent","Search the shop's products. Every result carries this customer's own price (their price group, quantity breaks, sales and live discounts) in the chosen currency, whether they can buy it, and its variants."],["get_product","shop/browse","30 calls/minute; 4 concurrent","One product in detail: the whole description, specifications, variants, colours/sizes/styles, and the customisation questions that must be answered to order it (engraving, measurements...), with this customer's price. Use it after search_products, before create_order."],["get_currency","shop/browse","20 calls/minute","The shop's own currency, the other currencies it prices in with their exchange rates, and what its prices mean for tax."],["get_booking_availability","shop/browse","15 calls/minute","When a booking product (a person's time, a room, accommodation, hire items, seats) can be booked: the start times over a few days, with the people or resources available at each. The shop's own booking rules decide what is shown."],["get_booking_end_times","shop/browse","15 calls/minute","For a chosen start, the ends that can be booked (return times, or check-out dates for accommodation), each checked against the booking rules."],["create_order","shop/orders:draft","10 calls/minute","Create a draft order and get its FINAL PRICE from the shop's own calculation (member prices, discounts, promo code, shipping and tax). Or change your draft by giving its order_id: the lines and settings given replace the old ones. Returns the order, shipping options and anything still stopping it being submitted. Does not place the order."],["submit_order","shop/orders:submit","6 calls/minute","Place a draft order: it is priced again, and submitted only if the total is still expected_total. This commits the customer to the order, so only call it after the customer has accepted the total. Calling it again for a submitted order returns that order."],["get_profile","shop/profile:read","20 calls/minute","The customer's own saved details, as their profile page shows them: contact details and customer address, the default delivery address, the interest groups they may pick, and their preferred size, colour and style. Never the password."],["save_profile","shop/profile:write","6 calls/minute","Change the customer's own saved details: only the fields given are changed. The login email and password cannot be changed here. New orders start from the saved address and delivery address. Returns the saved details."],["get_orders","shop/orders:read","20 calls/minute","The customer's own submitted orders, newest first, as shown on their order pages: status, totals, what was paid, delivery and tracking, and the lines."],["cancel_order","shop/orders:cancel","4 calls/minute","Cancel one of the customer's submitted orders, if the shop allows it (not yet picked or shipped, and within the shop's cancellation time). The reason is required. Bookings are released and stock is put back; the merchant is told by email. A paid order is not refunded automatically: the merchant arranges that. Only call it after the customer has asked to cancel."],["repeat_order","shop/orders:draft","6 calls/minute","Start a new draft order from one of the customer's submitted orders, at today's prices and availability (items that can no longer be bought are left out and listed). The result is like create_order's: review it, then submit_order."],["contact_business","shop/contact","5 calls/hour","Send a message from the customer to the business, by email, from the customer's own account (their name and login email; the business replies to that address). Only when the customer asks you to contact the business: use their own words. The message is checked for spam before it is sent and may be refused; 5 messages an hour at most."]]}},{"title":"Placing an order","paragraphs":["1. search_products (or get_categories) to find product ids, each with this customer's own price, and whether they can buy it. Results flag has_variants, has_options, has_customisation and is_booking.","2. get_product for the detail: the whole description, specifications, variants, colours/sizes/styles and the customisation questions (engraving text, measurements, a choice of material...).","3. create_order with items [{product_id, quantity, colour, size, style, answers}]. It returns the order_id and the FINAL PRICE from the shop's own calculation (price group, quantity breaks, live discounts, promo code, shipping, tax, chosen currency), the shipping options, and what still stops the order. Call it again with the same order_id to change the draft.","4. When the customer accepts the total, submit_order with order_id and expected_total. If the price has moved since, nothing is submitted and the new total is returned. A repeated call returns the submitted order.","Nothing is bought until submit_order. Emails and other notices are not sent by this interface, and payment is left to the merchant's own terms."],"example":"{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":{\"name\":\"create_order\",\"arguments\":{\"items\":[{\"product_id\":200,\"quantity\":2,\"answers\":{\"1\":\"gold\",\"2\":\"For Anna\"}}],\"promo_code\":\"SPRING\",\"delivery_address\":{\"city\":\"Napier\"}}}}"},{"title":"Customised products and options","paragraphs":["A product with has_customisation has questions. get_product lists each with question_id, type, required, its choices (with price variation) and any only_asked_if rule. Give answers as {\"<question_id>\": answer} (a list for a several-answers question); an answer to a question with choices must be one of the listed values.","A product without variants that lists colours, sizes or styles needs one of each chosen. Products with variants are chosen with colour, size and style, or by the variant's own id.","The price changes with the answers, so always use the price create_order returns. Products assembled from a recipe, products that use up a material product, and questions that need a file upload cannot be ordered here; get_product says so."]},{"title":"Errors and warnings in the basket","paragraphs":["After every create_order, repeat_order and refused submit_order the shop's totals have been calculated and the result carries: errors (what stops the order being submitted: a shipping option to choose, a shipping error, a missing booking time, an unchosen option, unanswered required questions), warnings (messages from the calculation, such as stock or promotion notes) and line_issues [{line_id, product_id, title, errors, warnings}] saying which line each belongs to.","A draft with errors is still saved so everything wrong is reported at once; fix it with create_order and the same order_id. When there are no errors, submit_order may be called."]},{"title":"Delivery and pick up","paragraphs":["create_order works out the delivery options from the order's delivery address (the customer's saved one unless delivery_address is given). If there is only one valid option it is used. If there are several (for example standard, express and pick up) the result lists them in shipping_options with prices, and errors says to choose: ask the customer, then call create_order again with the same order_id and shipping_option_id. Options marked pickup are collected by the customer."]},{"title":"Paying, and cancelling","paragraphs":["An order the customer has not paid yet (submitted in the last 30 days) carries payment_url: the customer opens it in a browser to pay by one of the merchant's payment methods. submit_order does not take payment.","cancel_order cancels a submitted order if the shop allows it (not yet picked or shipped, within the shop's cancellation time) and a reason is given. Bookings are released and the merchant is emailed. A paid order is not refunded automatically; the merchant arranges that. To change a booking, cancel and order again."]},{"title":"Bookings","paragraphs":["A product with is_booking is booked, not just bought (a person's time, a room, accommodation, hire items, seats). get_booking_availability lists the start times and the people or resources free at each; get_booking_end_times lists the ends for a chosen start. Give the chosen time to create_order as booking{start, duration_minutes or end, resource_id}. Times are the merchant's local time without a zone, like 2026-10-20T09:00. The booking rules of the shop decide availability; the time is checked again when the order is submitted, and held and booked in one step with the order."]},{"title":"The customer's details","paragraphs":["get_profile returns the customer's saved contact details and customer address, their default delivery address, the interest groups they may pick and their preferred size, colour and style. save_profile changes only the fields given. The login email and password cannot be changed here (change them on the website). A new order starts from the saved address and delivery address, so keep them current."]},{"title":"Past orders","paragraphs":["get_orders returns the customer's own submitted orders as they see them on the website: status, dates, totals, paid and balance, delivery and tracking, and the lines. repeat_order starts a new draft from a past order at today's prices; items that can no longer be bought are listed in left_out."]},{"title":"Visitors who are not signed in","paragraphs":["The shop MCP above is for a signed-in customer. For a visitor who has not signed in, https://www.birdofprey.co.nz/shop-api/mcp/public needs no sign-in: it searches the shop as the public sees it (public prices, no orders) and, when the website takes messages, lets the assistant send one to the business (contact_business, 5 an hour, with spam protection). Its guide is a GET of that address."]},{"title":"Errors and rate limits","paragraphs":["JSON-RPC errors: -32602 invalid arguments, -32003 scope not allowed, -32010 refused by the shop's rules (not available to you, out of stock, minimum spend...), -32029 rate limited, -32603 the tool failed.","Tools have per-customer request and concurrency limits (see the table). Successful calls can include RateLimit headers and rate_limit metadata. A limited request returns HTTP 429 with Retry-After. A customer may have at most 20 draft orders open in a day."]}]}}