{"apiVersion":"v1","directorySlug":"peach-mcp-322aa2dd68","title":"Peach AI","description":null,"pageUrl":"https://www.mcpbundles.com/skills/peach-mcp-322aa2dd68","connectPath":"importable","connectAction":"import_server","catalogListing":false,"bundleSlug":null,"listingKind":"remote_mcp","kind":"provider","transportUrl":"https://mcp.peach.build/api/mcp","hostedMcpUrl":null,"toolCount":72,"toolInventoryStatus":"observed","refreshedAt":"2026-11-03T13:26:00.516172+00:00","registryName":"build.peach/peach-mcp-322aa2dd68","registryVersion":"2026.11.3","registryEligible":true,"tools":[{"name":"list_presets","description":"List peach video-grammar presets — 5 reference product films × 4 director-lens variants. Each realizes as a cinematic scroll animation on any scanned 3D product.","inputSchema":{"type":"object","properties":{}},"title":"List video presets","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_presets","displayName":"List video presets"},{"name":"get_preset","description":"Get the full shot-grammar for one preset id: energy/cutStyle/arc plus per-beat { shot, move, subject, motion, light, dur }.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}},"title":"Get preset details","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"get_preset","displayName":"Get preset details"},{"name":"realize_preset","description":"Enqueue a render job: realize a video-grammar preset as a cinematic scroll animation on a scanned 3D model, then scroll-capture the beat frames. Returns a jobId immediately — a render takes ~1–3 min, so poll get_render with the jobId for status + frames. Requires auth.","inputSchema":{"type":"object","required":["model"],"properties":{"beats":{"type":"integer"},"model":{"type":"string"},"presetId":{"type":"string"}}},"title":"Render video preset","logicalName":"realize_preset","displayName":"Render video preset","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"get_render","description":"Poll a render job by id. Returns { status: queued|running|done|error, progress }, and on done: the realized beat frames as inline image content blocks, the auto-score (cinemaGrade 0-100 + per-beat notes, when scoring is configured), and a storyboard clip URL. Requires auth.","inputSchema":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string"}}},"title":"Check render status","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"get_render","displayName":"Check render status"},{"name":"realize_matrix","description":"Batch enqueue: render every model × preset combination (max 12 jobs per call). Returns the jobId grid — poll each with get_render. The worker renders serially (~3-6 min per job). Requires auth.","inputSchema":{"type":"object","required":["models","presetIds"],"properties":{"beats":{"type":"integer"},"models":{"type":"array","items":{"type":"string"}},"presetIds":{"type":"array","items":{"type":"string"}}}},"title":"Batch render presets","logicalName":"realize_matrix","displayName":"Batch render presets","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"judge_render","description":"Client-side cinematography gate (scores on YOUR plan, no API credits): returns the judging rubric, the grammar, and the finished render's beat frames. YOU (the calling model) judge the frames against the rubric, then call submit_score with your JSON verdict to store it on the job. Requires auth.","inputSchema":{"type":"object","required":["jobId"],"properties":{"jobId":{"type":"string"}}},"title":"Get judging rubric","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"judge_render","displayName":"Get judging rubric"},{"name":"submit_score","description":"Store a cinematography score on a finished render job (the write-back half of judge_render). get_render then returns it as cinemaGrade/verdict/beatNotes. Requires auth.","inputSchema":{"type":"object","required":["jobId","cinemaGrade"],"properties":{"jobId":{"type":"string"},"verdict":{"type":"string"},"beatNotes":{"type":"array","items":{"type":"object","properties":{"beat":{"type":"integer"},"note":{"type":"string"},"grade":{"type":"integer"}}}},"cinemaGrade":{"type":"integer"}}},"title":"Submit render score","logicalName":"submit_score","displayName":"Submit render score","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"whoami","description":"Identify the current MCP caller: resolved userId, auth method (staff act-as, personal token, or oauth sign-in), job-store backend, and the hourly render cap. Call this first — every other user-plane tool needs a resolved userId.","inputSchema":{"type":"object","properties":{}},"title":"Identify caller","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"whoami","displayName":"Identify caller"},{"name":"list_scenes","description":"List YOUR saved scenes (peach 3D scroll-scenes), most recently updated first. Requires auth.","inputSchema":{"type":"object","properties":{"limit":{"type":"integer"}}},"title":"List your scenes","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_scenes","displayName":"List your scenes"},{"name":"get_scene","description":"Get one scene's full data doc by id — your own, or any public/builtin scene. The doc is often 50–400 KB: to understand a scene call summarize_scene, and to read specific fields call read_scene_path. Requires auth.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}},"title":"Get scene data","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"get_scene","displayName":"Get scene data"},{"name":"save_scene","description":"Create or overwrite one of YOUR scenes. Omit `id` to create a new scene; pass `id` to overwrite (must be one of yours). Requires auth.","inputSchema":{"type":"object","required":["name","data"],"properties":{"id":{"type":"string"},"data":{"type":"object"},"name":{"type":"string"}}},"title":"Save scene","logicalName":"save_scene","displayName":"Save scene","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"list_experiences","description":"List YOUR peach experiences (published sites/embeds), most recently updated first, each with its updated_at. Requires auth.","inputSchema":{"type":"object","properties":{"limit":{"type":"integer"}}},"title":"List experiences","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_experiences","displayName":"List experiences"},{"name":"get_experience","description":"Get one of YOUR experiences (or any public one) by id — metadata + live URL (for your own: also editorUrl, the scene the editor opens, and draftPreviewUrl, your unpublished draft — an unpublished page's liveUrl shows visitors \"not published yet\"), and the data doc when include_data is true: the DRAFT for your own experiences, the PUBLISHED version only for anyone else's. Requires auth.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"include_data":{"type":"boolean"}}},"title":"Get experience","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"get_experience","displayName":"Get experience"},{"name":"summarize_scene","description":"A ~2 KB summary of one scene instead of its full data doc (often 50–400 KB): the model (shape, label, and the uploaded asset's host + file name, never the signed URL), group/single mode and count, background, lighting, which post-FX are on, camera and controls, an inlined experience's section/UI/hotspot counts, key counts per authoring category (the author_scene_patch vocabulary) and per editor folder, and the five largest keys with their JSON Pointers. Use this instead of get_scene to understan","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}},"title":"Summarize scene","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"summarize_scene","displayName":"Summarize scene"},{"name":"read_scene_path","description":"Read the value at one JSON Pointer inside a scene's data doc — e.g. \"/bloomIntensity\" or \"/experience/sections/0\". Values up to 20,000 bytes come back inline; a larger object or array comes back as its child pointers with their byte sizes, largest first, so you can read one level deeper. Use it instead of get_scene when you need specific fields (get paths from summarize_scene or describe_scene_vocabulary). Same access as get_scene. Requires auth.","inputSchema":{"type":"object","required":["id","path"],"properties":{"id":{"type":"string"},"path":{"type":"string"}}},"title":"Read scene path","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"read_scene_path","displayName":"Read scene path"},{"name":"summarize_experience","description":"Where an experience document's bytes are and what its parts are called — the complement of outline_experience, repeating none of it: total bytes and the draft vs published sizes, bytes per top-level key (one key like savedWraps can hold most of a 400 KB doc), bytes per scene, each section's name, each UI block's template, headline and hotspot count, and the total hotspot count. Use it with outline_experience instead of get_experience(include_data). Your own experience: the draft by default, or t","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"published":{"type":"boolean"}}},"title":"Summarize experience","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"summarize_experience","displayName":"Summarize experience"},{"name":"list_models","description":"List YOUR uploaded 3D model assets, each with its id (for create_scene) and public url. Requires auth.","inputSchema":{"type":"object","properties":{"limit":{"type":"integer"}}},"title":"List 3D models","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_models","displayName":"List 3D models"},{"name":"list_experience_templates","description":"List the ready-made starter experiences that create_experience can copy with template_id. Requires auth.","inputSchema":{"type":"object","properties":{}},"title":"List experience templates","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_experience_templates","displayName":"List experience templates"},{"name":"create_experience","description":"Create a NEW experience on the user's account. Pass exactly one source: scene_id (one of their scenes, giving a one-section experience) or template_id (from list_experience_templates, giving a full copy of that template, scenes included). It starts PRIVATE and unpublished, and nothing existing changes. Edit it with patch_experience; publishing (patch_experience publish:true, or prepare_embed) also makes it public. Requires auth.","inputSchema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"scene_id":{"type":"string"},"template_id":{"type":"string"}}},"title":"Create experience","logicalName":"create_experience","displayName":"Create experience","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"duplicate_experience","description":"Copy an experience into the user's account: their own draft, or the published version of someone else's public one. Every scene is copied too, so editing the copy never touches the original. When the original belongs to another account, the files it uses from that owner's uploads (models, images, ad artwork) are copied into this account and the copy points at them, so it keeps working if the original owner deletes theirs; assetsCopied / assetsFailed report it. The copy starts PRIVATE and unpubli","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"title":"Duplicate experience","logicalName":"duplicate_experience","displayName":"Duplicate experience","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"set_experience_visibility","description":"Make one of the user's experiences private (unpublish: its link and every embed stop showing it to visitors) or public again. Only visibility changes, never content. Publishing already makes an experience public, so use this mainly to unpublish. Before making a live experience private, confirm with the user: sites embedding it go blank. Requires auth.","inputSchema":{"type":"object","required":["id","public"],"properties":{"id":{"type":"string"},"public":{"type":"boolean"}}},"title":"Change experience visibility","logicalName":"set_experience_visibility","displayName":"Change experience visibility","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"delete_experience","description":"PERMANENTLY delete one of the user's experiences. Its link, custom domains and every embed stop working, and it cannot be undone. Always ask the user first, then pass confirm_name exactly equal to the experience's name; a mismatch deletes nothing. Its scenes are kept; remove those separately with delete_scene. Requires auth.","inputSchema":{"type":"object","required":["id","confirm_name"],"properties":{"id":{"type":"string"},"confirm_name":{"type":"string"}}},"title":"Delete experience","logicalName":"delete_experience","displayName":"Delete experience","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"delete_scene","description":"PERMANENTLY delete one of the user's scenes. Refused while any experience still uses it, and the refusal names them. Always ask the user first, then pass confirm_name exactly equal to the scene's name. Requires auth.","inputSchema":{"type":"object","required":["id","confirm_name"],"properties":{"id":{"type":"string"},"confirm_name":{"type":"string"}}},"title":"Delete scene","logicalName":"delete_scene","displayName":"Delete scene","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"import_model","description":"Import a 3D model from a public https link into the user's model library, the same as uploading it in the editor. Accepts binary glTF (.glb) up to 50 MB only; a .gltf with separate files, .fbx, .obj or .usdz must be converted to .glb first. Importing the same file twice reuses the first copy. Returns model_id; next, create_scene { name, model_id }. Requires auth.","inputSchema":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"name":{"type":"string"}}},"title":"Import 3D model","logicalName":"import_model","displayName":"Import 3D model","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"generate_model","description":"Generate a 3D model with AI from a text prompt or a public https image link; it is saved to the user's model library like a model generated in the editor. Costs AI credits: 150 credits on the default engine (Designer plan and up), and takes about 5 minutes. The FIRST call without confirm only returns the quote (price, what the user gets, time) and charges nothing: show it to the user and ASK them. Only after they agree, call again with the same input and confirm: true — that charges the credits","inputSchema":{"type":"object","properties":{"prompt":{"type":"string"},"confirm":{"type":"boolean"},"image_url":{"type":"string"}}},"title":"Generate 3D model","logicalName":"generate_model","displayName":"Generate 3D model","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"get_generation","description":"Check a model generation started with generate_model. While it runs, status is running with its stage; when the job moves to its next stage the result carries a NEW job_id — poll again with that one. When done, the model is already saved to the user's library and the result has model_id and url for create_scene { name, model_id }. A failed generation says whether its credits were refunded; status save_failed means the model exists but the save failed — call again with the same job_id to retry (n","inputSchema":{"type":"object","required":["job_id"],"properties":{"job_id":{"type":"string"}}},"title":"Get generation status","logicalName":"get_generation","displayName":"Get generation status","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"create_scene","description":"Create a new scene on the user's account from a model in their library (model_id from list_models or import_model). Returns the scene id. Style it with author_scene_patch, look at it with preview_scene, then give it a page with create_experience { name, scene_id }. Requires auth.","inputSchema":{"type":"object","required":["name","model_id"],"properties":{"name":{"type":"string"},"model_id":{"type":"string"}}},"title":"Create scene","logicalName":"create_scene","displayName":"Create scene","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"get_scan","description":"Get the feature-scan and part-scan analysis for a shape/model id — the geometry intel the video-grammar renderer uses to place shots. Requires auth.","inputSchema":{"type":"object","required":["shape"],"properties":{"shape":{"type":"string"}}},"title":"Get shape scan","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"get_scan","displayName":"Get shape scan"},{"name":"drive_editor","description":"Enqueue a scripted drive of the peach editor: a sequence of ops (patch/shape/material/videoPreset/chatTool/scroll/wait/snap) the render worker replays headlessly against a live editor session. Returns a jobId immediately — poll get_render for status/frames. Requires auth.","inputSchema":{"type":"object","required":["script"],"properties":{"model":{"type":"string"},"notes":{"type":"string"},"script":{"type":"array","items":{"type":"object","required":["op"],"properties":{"op":{"enum":["patch","shape","material","videoPreset","chatTool","scroll","wait","snap"],"type":"string"},"args":{"type":"object"}}}}}},"title":"Drive editor script","logicalName":"drive_editor","displayName":"Drive editor script","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"editor_command","description":"Run commands directly against YOUR currently open peach editor tab over the live bridge (not a queued render) — set a material, click a chat tool, or the capture-only tool \"screenshot\" (returns the current viewport, no edit). Pass ONE command as `tool`+`args`, or batch several as `commands` (1-10, run in order over a single bridge connection — much faster than separate calls; stops at the first failure; the screenshot that comes back is the last command's, i.e. the end state). Chat tools that sp","inputSchema":{"type":"object","properties":{"args":{"type":"object"},"tool":{"type":"string"},"confirm":{"type":"boolean"},"commands":{"type":"array","items":{"type":"object","required":["tool"],"properties":{"args":{"type":"object"},"tool":{"type":"string"},"confirm":{"type":"boolean"}}}}}},"title":"Run editor command","logicalName":"editor_command","displayName":"Run editor command","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"editor_status","description":"Check whether YOUR editor tab is currently connected to the live bridge (required for editor_command). Requires auth.","inputSchema":{"type":"object","properties":{}},"title":"Check editor status","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"editor_status","displayName":"Check editor status"},{"name":"list_flow_commands","description":"List the Flow/Blueprint verbs and whole-document generators reachable through editor_command / drive_editor's `tool` argument (or a drive_editor chatTool op's `name`) — never a separate write path, the same COMMANDS table the builder chat itself calls. Includes: `new_experience` (start a BRAND NEW experience from a prompt — the create-from-chat front door), `write_experience`, `import_reference`, `run_ui_loop`, `regenerate_chapter_ui`, and one `flow_<verb>` per chapter-editing command (add/reord","inputSchema":{"type":"object","properties":{"all":{"type":"boolean"},"name":{"type":"string"}}},"title":"List flow commands","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_flow_commands","displayName":"List flow commands"},{"name":"poll_chat","description":"Long-poll YOUR builder chat for the next pending \"MCP — my Claude\" turn posted from the peach builder tab. The server HOLDS the request up to waitMs (default 8000, max 15000) and answers the moment a turn arrives; { none: true } means the wait elapsed empty — loop straight back for as long as you are acting as the user's in-builder assistant (waitMs: 0 restores an immediate check). When a turn comes back, fulfil it with the other user-plane tools, then answer with reply_chat. Requires auth.","inputSchema":{"type":"object","properties":{"waitMs":{"type":"integer"}}},"title":"Poll builder chat","logicalName":"poll_chat","displayName":"Poll builder chat","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"reply_chat","description":"Answer a builder-chat turn claimed via poll_chat, delivering `text` (or `error` on failure) into the user's builder chat UI. Call once per turn, when you are done acting on it — the turn must still be in \"working\" status (i.e. yours, from your own poll_chat). Requires auth.","inputSchema":{"type":"object","required":["turnId"],"properties":{"text":{"type":"string"},"error":{"type":"string"},"turnId":{"type":"string"}}},"title":"Reply to builder chat","logicalName":"reply_chat","displayName":"Reply to builder chat","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"chat_progress","description":"Post an interim progress note on the builder-chat turn you're handling (shown live under the user's \"Waiting…\" state). Optional; call it as you work. Requires auth.","inputSchema":{"type":"object","required":["turnId","note"],"properties":{"note":{"type":"string"},"turnId":{"type":"string"}}},"title":"Post chat progress","logicalName":"chat_progress","displayName":"Post chat progress","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"list_embeddable","description":"List the user's PUBLISHED experiences that can be embedded on a site — id, name, live URL, and whether each is publicly reachable. Call this first when the user asks to put a Peach scene on a page; never guess an id. Requires auth.","inputSchema":{"type":"object","properties":{"limit":{"type":"integer"}}},"title":"List embeddable experiences","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_embeddable","displayName":"List embeddable experiences"},{"name":"get_embed_snippet","description":"Get the ready-to-paste embed code for one experience, tailored to where it is going. Targets: html, background, webflow, framer, react, next, shopify, figma, llm. Write the returned snippet into the user's project verbatim — it is a live reference to the hosted experience, not a copy of the scene. Also returns the rules you must follow when placing it. Requires auth.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"target":{"enum":["html","background","webflow","framer","react","next","shopify","figma","llm"],"type":"string"},"apexHost":{"type":"string"}}},"title":"Get embed snippet","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"get_embed_snippet","displayName":"Get embed snippet"},{"name":"get_embed_rules","description":"The rules governing how a Peach embed may be placed and modified: what must be copied exactly, what must never be done to it, and what host-page CSS/tooling to ignore. Read this before writing or editing embed markup. Open — no auth needed.","inputSchema":{"type":"object","properties":{}},"title":"Get embed rules","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"get_embed_rules","displayName":"Get embed rules"},{"name":"verify_embed","description":"Check an embed. Without `url`: confirms the experience exists, is published and is publicly reachable (it does NOT look at any page). With `url` (the page you put it on): also reports whether Peach has actually SEEN the embed load on that page's origin — the runtime reports every load — and `ok` is true only when it has, and HOW it rendered there: `mountedAs` inline (in the page) or iframe (the framed fallback), with `mountReason` and a `mountNote` naming the page setting to change. A page nobod","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"url":{"type":"string"}}},"title":"Verify embed","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"verify_embed","displayName":"Verify embed"},{"name":"prepare_embed","description":"Make an experience safe to embed in someone else's page: turns the scene's camera ZOOM and PAN off, leaving rotate alone. Do this BEFORE handing over an embed snippet — a zoomable scene swallows the visitor's scroll wheel and pinch the moment the pointer crosses it, so the host page stops scrolling, which is the most common reason an embed feels broken. Idempotent: re-running when it is already safe changes nothing. Requires auth.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"mode":{"enum":["scroll-safe","viewer","direct"],"type":"string"},"publish":{"type":"boolean"},"gate_label":{"type":"string"},"sceneIndex":{"type":"integer"}}},"title":"Prepare embed","logicalName":"prepare_embed","displayName":"Prepare embed","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"outline_experience","description":"The SKELETON of an experience — every scene and section with the JSON Pointer that addresses it, plus a few highlight fields per scene. A few hundred bytes instead of the full document, which for a real experience is ~400KB. Call this to find the path you want, then edit with patch_experience. Requires auth.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"published":{"type":"boolean"}}},"title":"Outline experience","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"outline_experience","displayName":"Outline experience"},{"name":"read_experience_path","description":"Read the value at one JSON Pointer inside an experience document — e.g. \"/sections/0\" or \"/scenes/0/data/color\". Values up to 20,000 bytes come back inline; a larger object or array comes back as its child pointers with their byte sizes, largest first, so you can read one level deeper; a larger STRING is read in windows with offset and length. Pass `fields` to list an array of objects compactly (e.g. \"/savedWraps\" with [\"id\",\"name\"]). Use it to inspect just the part you intend to change instead","inputSchema":{"type":"object","required":["id","path"],"properties":{"id":{"type":"string"},"path":{"type":"string"},"fields":{"type":"array","items":{"type":"string"}},"length":{"type":"integer","minimum":1},"offset":{"type":"integer","minimum":0},"published":{"type":"boolean"}}},"title":"Read experience path","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"read_experience_path","displayName":"Read experience path"},{"name":"patch_experience","description":"Edit one of YOUR experiences SURGICALLY, by JSON Pointer — the normal way to change an embedded experience. Send only the ops you want; everything else is left byte-for-byte untouched. Prefer this over update_experience: a real experience document is ~400KB, and replacing it whole wastes context and risks clobbering parts you never meant to touch. Ops: set (create/replace), remove, append (push onto an array), insert ({index, item}). Get paths from outline_experience. The result is schema-valida","inputSchema":{"type":"object","required":["id","ops"],"properties":{"id":{"type":"string"},"ops":{"type":"array","items":{"type":"object","required":["op","path"],"properties":{"op":{"enum":["set","remove","append","insert"],"type":"string"},"path":{"type":"string"},"value":{}}}},"dry_run":{"type":"boolean"},"publish":{"type":"boolean"}}},"title":"Patch experience","logicalName":"patch_experience","displayName":"Patch experience","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"edit_cab_ad","description":"Read or edit the cab AD artwork of one of your experiences — the London-cab wrap made in the Template Maker: the scene's ad (its cab_doc) and every saved wrap in the configurator library. action \"list\" returns each ad with its target (\"scene:<ref>\" or \"wrap:<id>\"), whether the cab paints it now (active — only while the scene wears the maker document), its projection (mode), background and layers (id, type, surface, role, text, colour, font, size, position; images as host + file only). action \"up","inputSchema":{"type":"object","required":["id","action"],"properties":{"bg":{"type":"string"},"id":{"type":"string"},"add":{"type":"array","items":{"type":"object","required":["type"],"properties":{"role":{"enum":["logo","headline","body","image","bg"],"type":"string"},"type":{"enum":["text","rectangle","line","circle","ellipse","image"],"type":"string"},"surface":{"enum":["side","all","roof","bonnet","back"],"type":"string"}}}},"order":{"type":"array","items":{"type":"string"}},"action":{"enum":["list","update"],"type":"string"},"layers":{"type":"array","items":{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}}},"remove":{"type":"array","items":{"type":"string"}},"target":{"type":"string"},"dry_run":{"type":"boolean"},"publish":{"type":"boolean"},"published":{"type":"boolean"}}},"title":"Edit cab ad","logicalName":"edit_cab_ad","displayName":"Edit cab ad","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"list_sections","description":"The PAGE SECTIONS of one of your experiences, in the order visitors see them: each section's id, template, headline and how many text fields and hotspots it has, plus the header and the 3D scroll beats underneath. Pass section_id to see one section in full: every text field (key, label, current value — including template placeholder copy that is showing because nothing was written yet) and every hotspot with its slot. Call this before edit_section, edit_hotspot, edit_interaction or set_experienc","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"published":{"type":"boolean"},"section_id":{"type":"string"}}},"title":"List experience sections","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_sections","displayName":"List experience sections"},{"name":"edit_section","description":"Add, rewrite, move or remove a PAGE SECTION without JSON paths. add: a new section from a template (template_id), optionally with its copy, placed after a section or at an index. update: replace copy fields (text) and/or style fields (style — accent, colours, fonts, sizes, toggles; see list_sections section_id) by key, e.g. text {\"title\":\"Acme One\",\"cta\":\"Pre-order\"} — keys come from list_sections section_id. move: to an index, or direction up/down. remove: delete a section (the last one is neve","inputSchema":{"type":"object","required":["id","action"],"properties":{"id":{"type":"string"},"text":{"type":"object"},"after":{"type":"string"},"index":{"type":"number"},"style":{"type":"object"},"action":{"enum":["add","update","move","remove"],"type":"string"},"dry_run":{"type":"boolean"},"publish":{"type":"boolean"},"direction":{"enum":["up","down"],"type":"string"},"section_id":{"type":"string"},"template_id":{"type":"string"}}},"title":"Edit section","logicalName":"edit_section","displayName":"Edit section","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"edit_hotspot","description":"Add, change or remove a HOTSPOT (a pin on the 3D model with a label and optional info panel) on a page section. add: needs a label; part names the model part to attach to (omit to auto-place); goes into the first free of six slots, and on a section that does not draw pins itself it switches that section's hotspots on (style \"hotspots\" rings or \"callouts\" leader lines). update/remove: address the hotspot by slot from list_sections. Fields a section does not show (e.g. price outside the editorial","inputSchema":{"type":"object","required":["id","action","section_id"],"properties":{"id":{"type":"string"},"cta":{"type":"string"},"ref":{"type":"string"},"body":{"type":"string"},"href":{"type":"string"},"part":{"type":"string"},"slot":{"type":"number"},"image":{"type":"string"},"label":{"type":"string"},"price":{"type":"string"},"specs":{"type":"string"},"style":{"enum":["hotspots","callouts"],"type":"string"},"action":{"enum":["add","update","remove"],"type":"string"},"dry_run":{"type":"boolean"},"publish":{"type":"boolean"},"section_id":{"type":"string"}}},"title":"Edit hotspot","logicalName":"edit_hotspot","displayName":"Edit hotspot","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"edit_interaction","description":"Make the 3D react to a gesture on a page section: hover the model and it hops, click a part and the camera frames it, hold and a panel opens. One slot = one gesture, up to twelve per section. set: pass `on` (the gesture), `do` (what happens) and, for a pointer gesture, `target` (stage/model/part); everything else rides in `fields`, keyed by the suffixes list_sections publishes for that pair (hover → animate takes Anim: \"bounce\", Mode, AnimDur; a panel takes PanelTitle, PanelBody). A wrong value","inputSchema":{"type":"object","required":["id","action","section_id"],"properties":{"do":{"type":"string"},"id":{"type":"string"},"on":{"type":"string"},"slot":{"type":"number"},"action":{"enum":["set","remove"],"type":"string"},"fields":{"type":"object"},"target":{"type":"string"},"dry_run":{"type":"boolean"},"publish":{"type":"boolean"},"section_id":{"type":"string"}}},"title":"Edit interaction","logicalName":"edit_interaction","displayName":"Edit interaction","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"set_experience_text","description":"Replace ONE piece of copy anywhere on the page — the fastest way to swap placeholder text for real copy. target is \"<section_id>.<fieldKey>\": \"header.title\", \"ui_abc_0.subtitle\", or a live hotspot's copy such as \"ui_abc_0.c2Body\". Get targets from list_sections section_id. Saves a draft unless publish:true. Requires auth.","inputSchema":{"type":"object","required":["id","target","text"],"properties":{"id":{"type":"string"},"text":{"type":"string"},"target":{"type":"string"},"dry_run":{"type":"boolean"},"publish":{"type":"boolean"}}},"title":"Set experience text","logicalName":"set_experience_text","displayName":"Set experience text","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"get_experience_fonts","description":"The FONTS of one of your experiences: the page styleguide's heading and body font (name, whether it is a Google, built-in or uploaded font, category) and their weights, every section that overrides them with its section id, the section ids you can override, and any uploaded fonts on the page you can reuse by name. Call this before set_experience_fonts. Requires auth.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"published":{"type":"boolean"}}},"title":"Get experience fonts","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"get_experience_fonts","displayName":"Get experience fonts"},{"name":"set_experience_fonts","description":"Change the FONTS of a page by font name — no JSON paths. Without section_id: the page styleguide, which every section follows unless it overrides it (heading_font, body_font, heading_weight, body_weight). Setting only the heading keeps the body font where it was. With section_id (from get_experience_fonts, \"header\" included): that one section's override (heading_font, body_font, heading_weight), or clear_section:true to make it follow the styleguide again. Font names are matched case-insensitive","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"dry_run":{"type":"boolean"},"publish":{"type":"boolean"},"body_font":{"type":"string"},"section_id":{"type":"string"},"body_weight":{"enum":[300,400,500,600,700,800,900],"type":"number"},"heading_font":{"type":"string"},"clear_section":{"type":"boolean"},"heading_weight":{"enum":[300,400,500,600,700,800,900],"type":"number"},"create_styleguide":{"type":"boolean"}}},"title":"Set experience fonts","logicalName":"set_experience_fonts","displayName":"Set experience fonts","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"list_fonts","description":"Search the fonts set_experience_fonts accepts: the Google Fonts catalog the player loads, most popular first, with each family's category and weights, plus the built-in stacks. query matches part of a name; category narrows to sans-serif, serif, display, handwriting or monospace. Returns at most 50. Requires auth.","inputSchema":{"type":"object","properties":{"limit":{"type":"number"},"query":{"type":"string"},"category":{"enum":["sans-serif","serif","display","handwriting","monospace"],"type":"string"}}},"title":"List fonts","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_fonts","displayName":"List fonts"},{"name":"publish_scene_page","description":"Publish the page kept inside one of your editor scenes (the editor's ?edit= id) — what the editor's own Publish button does: the page goes to the scene's linked experience, or, the first time, a public experience is created, linked to the scene and published. Returns the experience id and its live /x URL. Saved wraps from the editor's browser library cannot be embedded from here; wrapWarning says so when the page names some. Requires auth.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}},"title":"Publish scene page","logicalName":"publish_scene_page","displayName":"Publish scene page","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"wraps","description":"The WRAP LIBRARY of a page — the cab wraps its configurator can offer — by experience id or editor scene id. list: every wrap the page can name, with source built-in (the template's own), saved (embedded in this document, what visitors get), or library (in your editor library but not yet in this document — embed it), plus mode side|full, layerCount and where the pickers use it. create: a new saved wrap from what the scene currently wears (from \"scene\": its Template Maker document or custom artwo","inputSchema":{"type":"object","required":["id","action"],"properties":{"id":{"type":"string"},"from":{"type":"string"},"mode":{"enum":["side","full"],"type":"string"},"label":{"type":"string"},"action":{"enum":["list","create","embed","delete","rename"],"type":"string"},"wrapId":{"type":"string"},"publish":{"type":"boolean"}}},"title":"Wrap library","logicalName":"wraps","displayName":"Wrap library","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"copy_saved_wraps","description":"Copy the SAVED WRAP LIBRARY (/savedWraps — the cab wraps the editor saved, with their swatches and images) from one of your documents into another, merged by wrap id: wraps the target already has are skipped and nothing is overwritten. Either side may be an experience id or the editor scene id that holds a page. This is the one way to move a saved wrap between pages from here: a raw write of /savedWraps is refused as an asset reference because the server cannot tell your own uploads from someone","inputSchema":{"type":"object","required":["from","to"],"properties":{"to":{"type":"string"},"from":{"type":"string"},"publish":{"type":"boolean"},"from_published":{"type":"boolean"}}},"title":"Copy saved wraps","logicalName":"copy_saved_wraps","displayName":"Copy saved wraps","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"set_experience_theme","description":"Set a page's THEME — its styleguide colours and fonts — in one checked call, no JSON paths. Pass only the tokens you want to change; every other token is left as it is. colors take #hex (bandA/surfaceA: 0–1); fonts take a font name from list_fonts and weights 100–900; radii take corner radii in px (button 999 = pill). On a page with no styleguide yet, either name a base preset to start from or give at least colors.fg, colors.bandHex and colors.surfaceHex (otherwise those areas would paint white)","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"base":{"type":"string"},"fonts":{"type":"object","properties":{"body":{"type":"string"},"mono":{"type":"string"},"heading":{"type":"string"},"bodyWeight":{"type":"number"},"headingWeight":{"type":"number"}}},"radii":{"type":"object","properties":{"card":{"type":"number"},"field":{"type":"number"},"button":{"type":"number"}}},"colors":{"type":"object","properties":{"fg":{"type":"string"},"bandA":{"type":"number"},"btnBg":{"type":"string"},"btnFg":{"type":"string"},"accent":{"type":"string"},"fgMute":{"type":"string"},"accent2":{"type":"string"},"bandHex":{"type":"string"},"surfaceA":{"type":"number"},"surfaceHex":{"type":"string"}}},"dry_run":{"type":"boolean"},"publish":{"type":"boolean"}}},"title":"Set experience theme","logicalName":"set_experience_theme","displayName":"Set experience theme","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"update_experience","description":"Rename an experience, or REPLACE its whole data document. For editing an existing document prefer patch_experience — whole-document replacement means reading and rewriting ~400KB to change one field. Use this for a rename, or when you are authoring a document from scratch. Requires auth.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"data":{"type":"object"},"name":{"type":"string"},"publish":{"type":"boolean"}}},"title":"Update experience","logicalName":"update_experience","displayName":"Update experience","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"get_entitlement","description":"What the user's plan allows for an experience: whether code export is available, whether it is managed (agent-edited rather than exported), how many embed origins are permitted, and whether the Peach badge shows. Check this before telling a user they can self-host. Requires auth.","inputSchema":{"type":"object","properties":{}},"title":"Get plan entitlement","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"get_entitlement","displayName":"Get plan entitlement"},{"name":"list_embed_domains","description":"List the origins that have loaded an experience's embed, and whether each is allowed to render. Origins self-register on first load; past the plan's limit new ones land blocked. Use this to see where an experience is actually being used. Requires auth.","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}},"title":"List embed domains","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_embed_domains","displayName":"List embed domains"},{"name":"authorize_embed_domain","description":"Allow an origin to render an experience — call this after embedding it on a site so the page keeps working past the auto-registration limit. Pass the site origin (scheme://host, no path). Requires auth.","inputSchema":{"type":"object","required":["id","origin"],"properties":{"id":{"type":"string"},"origin":{"type":"string"}}},"title":"Authorize embed domain","logicalName":"authorize_embed_domain","displayName":"Authorize embed domain","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"block_embed_domain","description":"Stop an origin from rendering an experience — use when an embed has been copied onto a site the user did not authorize. Requires auth.","inputSchema":{"type":"object","required":["id","origin"],"properties":{"id":{"type":"string"},"origin":{"type":"string"}}},"title":"Block embed domain","logicalName":"block_embed_domain","displayName":"Block embed domain","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"author_scene_patch","description":"Author a Peach 3D scene SEMANTICALLY, server-side, with no browser open — the headless equivalent of talking to the in-product assistant. Send a patch grouped by category (lighting, background, shape, model, motion, prop, material, post_fx, camera, camera_controls, lights, exposure, fog, shadows, render_quality, screen_space, atmosphere, physics, lens_effects, particle_effects, distortion_effects, stylize_effects, backdrop_settings, backdrop_shaders, model_material, model_effects, model_options,","inputSchema":{"type":"object","required":["patch"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"patch":{"type":"object"},"dry_run":{"type":"boolean"}}},"title":"Author scene patch","logicalName":"author_scene_patch","displayName":"Author scene patch","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"describe_scene_vocabulary","description":"The machine-readable catalog of everything author_scene_patch may write. Without arguments: an index of every category with its key count, the full keys of the nine core categories, and the named exclusion groups. With { category }: that category's keys, what each key is checked against (enum / number / color / boolean / string / whitelist-only), the option lists for enum keys, and advisory value ranges. Read this before authoring — it covers the in-product Peach assistant's write surface and ev","inputSchema":{"type":"object","properties":{"category":{"enum":["lighting","background","shape","model","motion","prop","material","post_fx","camera","camera_controls","lights","exposure","fog","shadows","render_quality","screen_space","atmosphere","physics","lens_effects","particle_effects","distortion_effects","stylize_effects","backdrop_settings","backdrop_shaders","model_material","model_effects","model_options","prop_settings"],"type":"string"}}},"title":"Describe scene vocabulary","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"describe_scene_vocabulary","displayName":"Describe scene vocabulary"},{"name":"describe_look_vocabulary","description":"What a LOOK may change, and what it may never change. A Look is one imagined appearance of a WHOLE scene — backdrop and background, every light, the effects, the camera, the hero's material and colorway, and each prop's FINISH (roughness, metalness, transmission, sheen, clearcoat, iridescence, texture scale) — over a set whose LAYOUT is frozen: which props exist, where they stand, their scale/rotation/count, the hero's shape and pose, and render quality. Without arguments: the free categories wi","inputSchema":{"type":"object","properties":{"category":{"type":"string"}}},"title":"Describe look vocabulary","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"describe_look_vocabulary","displayName":"Describe look vocabulary"},{"name":"apply_look","description":"Apply one Look — atomically — either to YOUR OPEN EDITOR TAB (target \"editor\", the default: the same live bridge editor_command drives, and it photographs the result so the picture you show is a render of the thing, never a stand-in) or to one of your saved scenes (target \"scene\", the same write path and ownership check author_scene_patch uses). A frozen key, an unknown key, or a value outside its enum/range rejects the WHOLE look and nothing is applied; every rejection comes back at once, and a","inputSchema":{"type":"object","required":["look"],"properties":{"look":{"type":"object"},"target":{"enum":["editor","scene"],"type":"string"},"capture":{"type":"boolean"},"dry_run":{"type":"boolean"},"scene_id":{"type":"string"}}},"title":"Apply look","logicalName":"apply_look","displayName":"Apply look","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"save_look","description":"Save one Look, with its picture, to the user's own library — the shot is re-encoded to WebP and stored, the look is stored as its patch, so a saved look can be re-applied and re-photographed later. Pass `model` on the look (and `run`) when the look belongs to a bake-off, so list_looks can group a run by the model that imagined it. The patch is re-validated on the way in: a look that would not apply is not stored. Requires auth.","inputSchema":{"type":"object","required":["look"],"properties":{"ms":{"type":"integer"},"run":{"type":"string"},"look":{"type":"object"},"shot":{"type":"string"},"rejected":{"type":"array","items":{"type":"object"}},"scene_id":{"type":"string"},"scene_name":{"type":"string"}}},"title":"Save look","logicalName":"save_look","displayName":"Save look","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"list_looks","description":"Read your saved Looks back, newest first. With { group_by: \"model\" } it returns the study gallery's manifest instead — { run, scene, models: [{ model, looks: [{ id, name, blurb, shot, patch, rejected, ms }] }] } — so a bake-off page is a read of the store, never a hand-kept list. Requires auth.","inputSchema":{"type":"object","properties":{"run":{"type":"string"},"limit":{"type":"integer"},"group_by":{"enum":["model"],"type":"string"},"scene_id":{"type":"string"}}},"title":"List looks","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_looks","displayName":"List looks"},{"name":"list_hero_templates","description":"List the abstract-hero scene templates available to apply with apply_hero_template. Returns `status: \"catalog-pending\"` with an empty list where the catalog is not deployed yet — author directly with author_scene_patch in that case. Requires auth.","inputSchema":{"type":"object","properties":{}},"title":"List hero templates","annotations":{"readOnlyHint":true,"untrustedContentHint":true},"logicalName":"list_hero_templates","displayName":"List hero templates"},{"name":"apply_hero_template","description":"Apply one abstract-hero template to a scene. The template's patch runs the same clamp, enum and atomic-refusal machinery as author_scene_patch, over a wider key set: a builtin template is first-party data, so it may set ANY setting the editor schema declares — including the per-effect shader and particle keys describe_scene_vocabulary does not list, which is what most of these looks are made of. A key the editor schema does not declare, or a value outside its option list, is still refused loudly","inputSchema":{"type":"object","required":["templateId"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"dry_run":{"type":"boolean"},"templateId":{"type":"string"}}},"title":"Apply hero template","logicalName":"apply_hero_template","displayName":"Apply hero template","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"preview_page","description":"Enqueue a PAGE preview of an experience: the page a visitor sees — text, hotspots, sections and the live 3D scene — loaded in a browser and captured as stills at scroll points. This is the \"does my page actually look right\" loop after create_experience, edit_section or patch_experience. Published (default): the public /x page as served to visitors; anyone signed in may preview a public experience. draft:true: the owner's unpublished draft (owners only). Returns a jobId immediately; poll get_rend","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"draft":{"type":"boolean"},"device":{"enum":["desktop","mobile"],"type":"string"},"scroll_points":{"type":"array","items":{"type":"number","maximum":1,"minimum":0},"maxItems":8}}},"title":"Preview embed page","logicalName":"preview_page","displayName":"Preview embed page","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"preview_scene","description":"Enqueue a still PREVIEW render of one of YOUR saved scenes: the scene's settings applied to the default camera/lighting rig, captured as one image. This is the \"did my author_scene_patch actually look right\" loop — author_scene_patch writes the document but renders nothing. Returns a jobId immediately; poll get_render with it exactly as you would for realize_preset. Cost: 10 AI credits (worst case $0.10 at the top-up rate, $0.07 at the volume floor) per preview, charged to your account (personal","inputSchema":{"type":"object","required":["id"],"properties":{"id":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"}}},"title":"Preview scene render","logicalName":"preview_scene","displayName":"Preview scene render","annotations":{"untrustedContentHint":true,"readOnlyHint":true}},{"name":"chat_stream","description":"Stream the reply-so-far into the user's builder chat as you compose it — pass the FULL accumulated reply text each call (overwrites, not appends). The builder renders it live while the turn is still working. Optional; reply_chat still finalizes. Requires auth.","inputSchema":{"type":"object","required":["turnId","text"],"properties":{"text":{"type":"string"},"turnId":{"type":"string"}}},"title":"Stream chat reply","logicalName":"chat_stream","displayName":"Stream chat reply","annotations":{"untrustedContentHint":true,"readOnlyHint":true}}],"toolSource":"directory_preview","agentAction":"both","installLinks":[{"id":"cursor","label":"Cursor","href":"cursor://anysphere.cursor-deeplink/mcp/install?name=Peach-AI&config=eyJ1cmwiOiAiaHR0cHM6Ly9tY3AucGVhY2guYnVpbGQvYXBpL21jcCJ9"},{"id":"vscode","label":"VS Code","href":"vscode:mcp/install?%7B%22name%22%3A%22Peach-AI%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.peach.build%2Fapi%2Fmcp%22%7D"},{"id":"vscode-insiders","label":"VS Code Insiders","href":"vscode-insiders:mcp/install?%7B%22name%22%3A%22Peach-AI%22%2C%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.peach.build%2Fapi%2Fmcp%22%7D"},{"id":"claude-web","label":"Claude.ai","href":"https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=Peach+AI&connectorUrl=https%3A%2F%2Fmcp.peach.build%2Fapi%2Fmcp"},{"id":"codex","label":"Codex","copyText":"codex mcp add \"Peach-AI\" --url \"https://mcp.peach.build/api/mcp\""},{"id":"chatgpt","label":"ChatGPT","href":"https://www.mcpbundles.com/docs/integrations/chatgpt"}],"catalogUrl":"https://www.mcpbundles.com/api/directory/v1/servers/peach-mcp-322aa2dd68","callUrl":"https://www.mcpbundles.com/api/directory/v1/servers/peach-mcp-322aa2dd68/tools/{tool}","mcpServerUrl":"https://mcp.peach.build/api/mcp","liveCatalogUrl":"https://www.mcpbundles.com/api/directory/v1/servers/peach-mcp-322aa2dd68?live=1"}