# History Cake — MCP Integration Reference History Cake is a platform for geo-triggered audio tours: a "tour" contains ordered "stories" (narrated stops with coordinates and a trigger radius). This document is the complete reference for building tours through the History Cake MCP server. ## Connect Remote (Streamable HTTP, stateless): POST https://historycake.app/api/mcp Authorization: Bearer hc_mcp_... Accept: application/json, text/event-stream stdio bridge for clients without remote MCP support: npx -y @history-cake/mcp env: HISTORY_CAKE_API_KEY=hc_mcp_... (HISTORY_CAKE_URL optional) Claude Code: claude mcp add --transport http history-cake https://historycake.app/api/mcp --header "Authorization: Bearer hc_mcp_..." API keys are created in Studio → Settings → API tokens (creator or admin accounts only). Keys are shown once, stored hashed, and can be revoked or expired; role and ban status are re-checked on every request. ## Rules and limits - 120 requests per minute per token. - You can only manage tours you own (admins can manage all). - Media inputs are public HTTPS URLs; the server imports them into the History Cake media library (S3). No data: URLs, no private hosts. - No AI generation over MCP: write story text and narration scripts yourself, and supply narration audio as HTTPS files (or upload narration audio later in Studio). The audioScript field stores your narration script. - Publishing requires: every story has narration audio, the tour has a primary (cover) image, and non-flying tours have at least one "route" story. check_tour_publishable reports the exact blockers. ## Recommended workflow (from a researched document) 1. create_tour_bundle — tour fields + all stories (title, latitude, longitude, description, audioScript, role route/poi) in one call. 2. import_media_url + update_tour — set the cover image (primaryImageId). 3. import_media_url + update_story — set per-story narration audio (audioId) and images (primaryImageId). 4. check_tour_publishable — fix anything it lists. 5. publish_tour — the tour goes live at its public URL. ## Tools ### create_tour_bundle Create a complete History Cake tour in one call: tour fields, stories (with locations, narration scripts, role/POI status), tags, and media imported from HTTPS URLs. Created as a draft unless status is 'published' AND the tour passes publish validation (every story needs narration audio, the tour needs a cover image, and non-flying tours need at least one route stop) — otherwise it is saved as a draft and publishBlockers lists what's missing. This is the fastest way to turn a researched text document into a tour. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "tour": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "shortDescription": { "type": "string", "maxLength": 500 }, "description": { "type": "string", "maxLength": 30000 }, "status": { "default": "draft", "type": "string", "enum": [ "draft", "published" ] }, "navigationType": { "default": "driving", "type": "string", "enum": [ "driving", "walking", "flying" ] }, "storyOrderMode": { "default": "ordered", "type": "string", "enum": [ "ordered", "unordered" ] }, "estimatedDurationMinutes": { "type": "integer", "minimum": 0, "maximum": 10000 }, "tags": { "maxItems": 25, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "media": { "type": "object", "properties": { "primaryImageUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "primaryVideoUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "backgroundMusicUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "introAudioUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "outroAudioUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "gallery": { "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 }, "role": { "type": "string", "minLength": 1, "maxLength": 80 }, "orderIndex": { "type": "integer", "minimum": 0, "maximum": 1000 } }, "required": [ "url" ] } } } } }, "required": [ "name" ] }, "stories": { "default": [], "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 200 }, "shortDescription": { "type": "string", "maxLength": 500 }, "description": { "type": "string", "maxLength": 20000 }, "audioScript": { "type": "string", "maxLength": 12000 }, "latitude": { "type": "number", "minimum": -90, "maximum": 90 }, "longitude": { "type": "number", "minimum": -180, "maximum": 180 }, "city": { "type": "string", "maxLength": 120 }, "state": { "type": "string", "maxLength": 120 }, "zipCode": { "type": "string", "maxLength": 20 }, "triggerRadius": { "default": 500, "type": "number", "minimum": 10, "maximum": 10000 }, "historicalDate": { "type": "string", "maxLength": 100 }, "eventStartDate": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "eventEndDate": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "orderIndex": { "type": "integer", "minimum": 0, "maximum": 1000 }, "role": { "default": "route", "type": "string", "enum": [ "route", "poi" ] }, "poiCategory": { "type": "string", "enum": [ "bar", "restaurant", "trailhead", "viewpoint", "parking", "other" ] }, "tags": { "maxItems": 25, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "media": { "type": "object", "properties": { "primaryImageUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "primaryVideoUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "audioUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "gallery": { "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 }, "role": { "type": "string", "minLength": 1, "maxLength": 80 }, "orderIndex": { "type": "integer", "minimum": 0, "maximum": 1000 } }, "required": [ "url" ] } } } } }, "required": [ "title", "latitude", "longitude" ] } } }, "required": [ "tour" ] } ``` ### import_media_url Import a single HTTPS image, audio, or video URL into the History Cake media library (downloads to S3). Returns a mediaId you can set as a tour cover, story image, or narration audio via the update tools. Only public HTTPS URLs are allowed. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ``` ### create_story Add one story (a geo-triggered narration stop) to an existing tour: location, trigger radius, text, narration script, role (route stop or off-path point of interest), media from URLs, and tags. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 200 }, "shortDescription": { "type": "string", "maxLength": 500 }, "description": { "type": "string", "maxLength": 20000 }, "audioScript": { "type": "string", "maxLength": 12000 }, "latitude": { "type": "number", "minimum": -90, "maximum": 90 }, "longitude": { "type": "number", "minimum": -180, "maximum": 180 }, "city": { "type": "string", "maxLength": 120 }, "state": { "type": "string", "maxLength": 120 }, "zipCode": { "type": "string", "maxLength": 20 }, "triggerRadius": { "default": 500, "type": "number", "minimum": 10, "maximum": 10000 }, "historicalDate": { "type": "string", "maxLength": 100 }, "eventStartDate": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "eventEndDate": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "orderIndex": { "type": "integer", "minimum": 0, "maximum": 1000 }, "role": { "default": "route", "type": "string", "enum": [ "route", "poi" ] }, "poiCategory": { "type": "string", "enum": [ "bar", "restaurant", "trailhead", "viewpoint", "parking", "other" ] }, "tags": { "maxItems": 25, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "media": { "type": "object", "properties": { "primaryImageUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "primaryVideoUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "audioUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 } }, "required": [ "url" ] } ] }, "gallery": { "maxItems": 50, "type": "array", "items": { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 }, "role": { "type": "string", "minLength": 1, "maxLength": 80 }, "orderIndex": { "type": "integer", "minimum": 0, "maximum": 1000 } }, "required": [ "url" ] } } } }, "tourId": { "type": "string", "minLength": 1 } }, "required": [ "title", "latitude", "longitude", "tourId" ] } ``` ### update_tour Patch fields on a tour you own: name, descriptions, navigation type, story order mode, duration estimate, primary media (by mediaId from import_media_url), and tags (replaces all tags when provided; tags are created by name as needed). The public URL slug never changes after creation. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "tourId": { "type": "string", "minLength": 1 }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "shortDescription": { "anyOf": [ { "type": "string", "maxLength": 500 }, { "type": "null" } ] }, "description": { "anyOf": [ { "type": "string", "maxLength": 30000 }, { "type": "null" } ] }, "navigationType": { "type": "string", "enum": [ "driving", "walking", "flying" ] }, "storyOrderMode": { "type": "string", "enum": [ "ordered", "unordered" ] }, "estimatedDurationMinutes": { "anyOf": [ { "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" } ] }, "primaryImageId": { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "null" } ] }, "primaryVideoId": { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "null" } ] }, "backgroundMusicId": { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "null" } ] }, "introAudioId": { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "null" } ] }, "outroAudioId": { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "null" } ] }, "tags": { "maxItems": 25, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } } }, "required": [ "tourId" ] } ``` ### update_story Patch fields on a story you own: text, narration script (audioScript), location, trigger radius, dates, primary media (by mediaId), and tags (replaces all tags when provided). Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "storyId": { "type": "string", "minLength": 1 }, "title": { "type": "string", "minLength": 1, "maxLength": 200 }, "shortDescription": { "anyOf": [ { "type": "string", "maxLength": 500 }, { "type": "null" } ] }, "description": { "anyOf": [ { "type": "string", "maxLength": 20000 }, { "type": "null" } ] }, "audioScript": { "anyOf": [ { "type": "string", "maxLength": 12000 }, { "type": "null" } ] }, "latitude": { "type": "number", "minimum": -90, "maximum": 90 }, "longitude": { "type": "number", "minimum": -180, "maximum": 180 }, "city": { "anyOf": [ { "type": "string", "maxLength": 120 }, { "type": "null" } ] }, "state": { "anyOf": [ { "type": "string", "maxLength": 120 }, { "type": "null" } ] }, "zipCode": { "anyOf": [ { "type": "string", "maxLength": 20 }, { "type": "null" } ] }, "triggerRadius": { "type": "number", "minimum": 10, "maximum": 10000 }, "historicalDate": { "anyOf": [ { "type": "string", "maxLength": 100 }, { "type": "null" } ] }, "eventStartDate": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "eventEndDate": { "anyOf": [ { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, { "type": "null" } ] }, "primaryImageId": { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "null" } ] }, "primaryVideoId": { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "null" } ] }, "audioId": { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "null" } ] }, "tags": { "maxItems": 25, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } } }, "required": [ "storyId" ] } ``` ### get_tour Fetch full detail for a tour you own: all fields, tags, every story (with order, role, and whether it has audio/image/script yet), plus publishReadiness telling you exactly what still blocks publishing. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "tourId": { "type": "string", "minLength": 1 } }, "required": [ "tourId" ] } ``` ### get_my_tours List tours owned by the authenticated token's user (admins see all): id, slug, status, story count, and public URL. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "limit": { "default": 20, "type": "integer", "minimum": 1, "maximum": 100 } } } ``` ### get_story Fetch one story's complete content by id: full WYSIWYG `description` text, the private `audioScript` narration, short description, exact location and trigger radius, dates, resolved primary image/video/audio URLs, gallery items, tags, and every tour it belongs to (with its per-tour order and role). Use this to read or audit a story's existing wording before editing it with update_story — list_stories and get_tour only report whether content exists, not the text itself. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "storyId": { "type": "string", "minLength": 1 } }, "required": [ "storyId" ] } ``` ### list_stories List every story in a tour in listening order, with role (route/POI), location, short description, and whether each has narration audio, a primary image, or an audio script. Pass includeContent:true to also return each story's full description text and audioScript so you can audit a whole tour's writing in one call. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "tourId": { "type": "string", "minLength": 1 }, "includeContent": { "default": false, "type": "boolean" } }, "required": [ "tourId" ] } ``` ### reorder_stories Set the listening order of a tour's stories. Pass every story currently in the tour (use list_stories first) as an ordered array of storyIds. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "tourId": { "type": "string", "minLength": 1 }, "storyIds": { "minItems": 1, "maxItems": 500, "type": "array", "items": { "type": "string", "minLength": 1 } } }, "required": [ "tourId", "storyIds" ] } ``` ### set_story_role Mark a story as a route stop or an off-path point of interest (with a category like viewpoint/restaurant/trailhead) within one tour. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "tourId": { "type": "string", "minLength": 1 }, "storyId": { "type": "string", "minLength": 1 }, "role": { "type": "string", "enum": [ "route", "poi" ] }, "poiCategory": { "type": "string", "enum": [ "bar", "restaurant", "trailhead", "viewpoint", "parking", "other" ] } }, "required": [ "tourId", "storyId", "role" ] } ``` ### remove_story_from_tour Detach a story from a tour (the story itself is kept; it must remain in at least one tour). Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "tourId": { "type": "string", "minLength": 1 }, "storyId": { "type": "string", "minLength": 1 } }, "required": [ "tourId", "storyId" ] } ``` ### attach_media_to_story Attach an existing media item (mediaId) or import an HTTPS URL into a story's gallery, with optional caption and order. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "mediaId": { "type": "string", "minLength": 1 }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 }, "orderIndex": { "default": 0, "type": "integer", "minimum": 0, "maximum": 1000 }, "storyId": { "type": "string", "minLength": 1 } }, "required": [ "storyId" ] } ``` ### attach_media_to_tour Attach an existing media item (mediaId) or import an HTTPS URL into a tour's gallery, with optional caption, role, and order. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "mediaId": { "type": "string", "minLength": 1 }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "name": { "type": "string", "minLength": 1, "maxLength": 200 }, "description": { "type": "string", "maxLength": 1000 }, "altText": { "type": "string", "maxLength": 500 }, "caption": { "type": "string", "maxLength": 500 }, "orderIndex": { "default": 0, "type": "integer", "minimum": 0, "maximum": 1000 }, "tourId": { "type": "string", "minLength": 1 }, "role": { "default": "gallery", "type": "string", "minLength": 1, "maxLength": 80 } }, "required": [ "tourId" ] } ``` ### list_tags Browse existing content tags (optionally filtered by search) so you can reuse them instead of creating near-duplicates. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "search": { "type": "string", "maxLength": 100 }, "limit": { "default": 100, "type": "integer", "minimum": 1, "maximum": 200 } } } ``` ### check_tour_publishable Dry-run the publish validation for a tour. Returns ok plus the exact blocking errors (e.g. STORY_NO_AUDIO, NO_PRIMARY_IMAGE) and non-blocking warnings, without changing anything. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "tourId": { "type": "string", "minLength": 1 } }, "required": [ "tourId" ] } ``` ### publish_tour Validate and publish a tour you own. Fails with the list of blocking reasons if the tour isn't complete (run check_tour_publishable first). On success the tour goes live at its public URL. Input schema (JSON Schema): ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": { "tourId": { "type": "string", "minLength": 1 } }, "required": [ "tourId" ] } ```