{
  "capturedAt": "2026-09-28",
  "updatedAt": "2026-10-07",
  "groups": [
    {
      "id": "auth",
      "title": "Sign-in and status"
    },
    {
      "id": "libraries",
      "title": "Libraries"
    },
    {
      "id": "emails",
      "title": "Emails"
    },
    {
      "id": "images",
      "title": "Images"
    },
    {
      "id": "quality",
      "title": "Preview and quality"
    },
    {
      "id": "dynamic",
      "title": "Dynamic content"
    }
  ],
  "tools": [
    {
      "name": "stensul-check-auth",
      "group": "auth",
      "summary": "Reports whether the current session has a valid sign-in, where the token came from, and what to do next.",
      "scope": null,
      "access": "read",
      "notes": "Most hosts handle sign-in for you. When you connect through your instance URL (`https://mcp.stensul.com/<your-instance>/mcp`), the host sends the token with every request and this tool always reports `source: \"http-bearer\"`.",
      "params": [],
      "example": {
        "arguments": {}
      },
      "responseCaptured": true,
      "responseExample": {
        "hasToken": true,
        "source": "http-bearer",
        "baseUrl": "https://<your-instance>.stensul.com",
        "tenant": "<your-instance>",
        "expiresAt": null,
        "secondsRemaining": null,
        "nextStep": "Authenticated via HTTP bearer. You can call data tools now."
      },
      "responseNote": "Captured from a Stensul-internal tenant on 2026-09-28. Instance name replaced with a placeholder."
    },
    {
      "name": "stensul-get-auth-url",
      "group": "auth",
      "summary": "Starts the OAuth authorization-code flow and returns the sign-in URL the user opens in a browser.",
      "scope": null,
      "access": "write",
      "notes": "Hosts that support remote OAuth connectors (Claude, ChatGPT, Writer, Copilot Studio) run this flow themselves. The server tells the model to show the full URL to the user, never a summary of it.",
      "params": [
        {
          "name": "callback_url",
          "type": "string",
          "required": false,
          "description": "OAuth redirect URI override. Leave unset: the server builds the callback from its own base URL and your instance at runtime. Only for one-off local testing."
        }
      ],
      "example": {
        "arguments": {}
      },
      "responseDescription": "An authorization URL for your Stensul instance. After the user signs in and approves, the token is saved for the session."
    },
    {
      "name": "stensul-health",
      "group": "auth",
      "summary": "Checks credentials, connectivity and available scopes. Run it first when calls fail with authentication or access errors.",
      "scope": null,
      "access": "read",
      "notes": "On an instance-URL connection, where sign-in is handled by the host, this tool can report missing client credentials even while data tools work. Use `stensul-check-auth` to confirm the session instead.",
      "params": [],
      "example": {
        "arguments": {}
      },
      "responseDescription": "A checklist: whether credentials are configured, whether a token was obtained, which scopes the connection has, and whether the API is reachable."
    },
    {
      "name": "stensul-get-libraries",
      "group": "libraries",
      "summary": "Lists libraries, or returns everything needed to work in one: its modules, variations, Segment Set support, languages, tags and which email settings it enables.",
      "scope": "libraries:read",
      "access": "read",
      "notes": "Call it with no key to browse. Call it with `library_key` or `library_id` before building, because the library decides which settings fields are allowed (`subject_line_enabled`, `preheader_enabled`, `title_enabled`) and their character caps.",
      "params": [
        {
          "name": "library_key",
          "type": "string",
          "required": false,
          "description": "Library key, for example `acme_newsletters`. Returns that library's full context."
        },
        {
          "name": "library_id",
          "type": "string (24-char hex id)",
          "required": false,
          "description": "Library ID. Returns that library's full context."
        },
        {
          "name": "name",
          "type": "string",
          "required": false,
          "description": "List mode only: filter by library name."
        },
        {
          "name": "page_number",
          "type": "integer ≥ 1",
          "required": false,
          "description": "List mode only: page number."
        },
        {
          "name": "page_size",
          "type": "integer 1–30",
          "required": false,
          "description": "List mode only: results per page (max 30, default 15)."
        },
        {
          "name": "sort",
          "type": "string",
          "required": false,
          "description": "List mode only: `name`, `created_at` or `updated_at`; prefix `-` for descending."
        }
      ],
      "example": {
        "arguments": {
          "library_key": "acme_newsletters"
        }
      },
      "responseDescription": "List mode: a page of libraries. Library mode: the module inventory (by role, and as ordered keys with the variations each implements), the variations mode (`all` or `individual`) and named variations, `dca_segment_sets.supported`, supported languages, tags, enabled settings fields and their length caps."
    },
    {
      "name": "stensul-list-emails",
      "group": "emails",
      "summary": "Lists emails and templates with filters for name, type, library, tags, language, project and status.",
      "scope": "emails:read",
      "access": "read",
      "notes": "The default field set already covers browsing and picking. Leave `fields` unset unless you have a reason.",
      "params": [
        {
          "name": "q",
          "type": "string",
          "required": false,
          "description": "Search by name, author or tags."
        },
        {
          "name": "name",
          "type": "string",
          "required": false,
          "description": "Filter by email name."
        },
        {
          "name": "types",
          "type": "string[]",
          "required": false,
          "description": "Filter by email types, for example `[\"draft\", \"template\"]`."
        },
        {
          "name": "library_keys",
          "type": "string[]",
          "required": false,
          "description": "Filter by library keys."
        },
        {
          "name": "tags",
          "type": "string[]",
          "required": false,
          "description": "Filter by tags."
        },
        {
          "name": "tags_match_criteria",
          "type": "\"any\" | \"all\"",
          "required": false,
          "description": "Match criteria for the tags filter."
        },
        {
          "name": "language",
          "type": "string",
          "required": false,
          "description": "Filter by language, for example `en-US`."
        },
        {
          "name": "project_id",
          "type": "string (24-char hex id)",
          "required": false,
          "description": "Filter by project ID."
        },
        {
          "name": "is_archived",
          "type": "0 | 1",
          "required": false,
          "description": "Filter archived emails."
        },
        {
          "name": "has_been_uploaded",
          "type": "0 | 1",
          "required": false,
          "description": "Filter by uploaded status."
        },
        {
          "name": "has_modules_locked",
          "type": "0 | 1",
          "required": false,
          "description": "Filter by locked-modules status."
        },
        {
          "name": "include",
          "type": "string",
          "required": false,
          "description": "Include related resources, for example `outputs,snippets`."
        },
        {
          "name": "fields",
          "type": "string",
          "required": false,
          "description": "Comma-separated sparse fieldset. `id` is always included."
        },
        {
          "name": "sort",
          "type": "string",
          "required": false,
          "description": "Sort criteria; prefix `-` for descending."
        },
        {
          "name": "page_number",
          "type": "integer ≥ 1",
          "required": false,
          "description": "Page number."
        },
        {
          "name": "page_size",
          "type": "integer 1–30",
          "required": false,
          "description": "Results per page (max 30, default 15)."
        }
      ],
      "example": {
        "arguments": {
          "types": [
            "template"
          ],
          "library_keys": [
            "acme_newsletters"
          ],
          "page_size": 10
        }
      },
      "responseDescription": "A page of emails with name, type, tags, project, archived and upload status, locked and outdated modules, proof and approval state, library, language and last update."
    },
    {
      "name": "stensul-read-email",
      "group": "emails",
      "summary": "Reads an email or template. The default `plan` view returns its full structure with repeated styles and guardrails deduplicated, which is what you need before building or editing.",
      "scope": "emails:read",
      "access": "read",
      "notes": "Structure is modules → rows → columns → elements, in order. Elements of the same type in different modules can share an id, so always pair module id and element id. Keep the returned `save_version` and pass it to your next write so a stale edit fails instead of overwriting newer work.",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "Email ID."
        },
        {
          "name": "view",
          "type": "\"plan\" | \"raw\" | \"meta\"",
          "required": false,
          "description": "`plan` (default, compact and lossless), `raw` (undeduplicated, for diagnostics), `meta` (settings and metadata only)."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd",
          "view": "plan"
        }
      },
      "responseDescription": "Metadata, the module layout with `styleProfiles` and `guardrailProfiles` tables, a `visibility` field on elements that can be hidden, a `variations` block and the email's `save_version`. Custom elements expose editable variables, never markup."
    },
    {
      "name": "stensul-build-email",
      "group": "emails",
      "summary": "Creates a complete draft in one call: starts from a template or an existing email, sets the settings, uploads images by public URL and writes all the content.",
      "scope": "emails:write",
      "access": "write",
      "notes": "Read the source with `stensul-read-email` first: content is addressed by position against that read (module 1 is the first module). Guardrails are hard limits. Anything that violates one is declined and listed in the result, not reshaped. The result is a report: the draft and its edit link, what was applied, warnings, skipped slots, missing images, the new `save_version` and a preview.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "Name for the new email."
        },
        {
          "name": "template_id",
          "type": "string (24-char hex id)",
          "required": false,
          "description": "Start from this template. Exactly one of `template_id` or `copy_of`."
        },
        {
          "name": "copy_of",
          "type": "string (24-char hex id)",
          "required": false,
          "description": "Start from a copy of this email, content and images included. Exactly one of `template_id` or `copy_of`."
        },
        {
          "name": "library_key",
          "type": "string",
          "required": false,
          "description": "Required with `template_id`; optional with `copy_of` (defaults to the source library)."
        },
        {
          "name": "content",
          "type": "array",
          "required": false,
          "description": "Content by position: `[{ position, style?, elements: [{ position, type, text?, content?, imageContent?, link?, visibility?, url_tracking_parameters? }] }]`. Element `type` is `text`, `button`, `image` or `divider` and is cross-checked against the draft."
        },
        {
          "name": "subject_line",
          "type": "string",
          "required": false,
          "description": "Only if the library enables subject lines."
        },
        {
          "name": "preheader",
          "type": "string",
          "required": false,
          "description": "Only if the library enables preheaders."
        },
        {
          "name": "title",
          "type": "string",
          "required": false,
          "description": "Only if the library enables titles."
        },
        {
          "name": "language",
          "type": "string",
          "required": false,
          "description": "Language code, for example `en-US`."
        },
        {
          "name": "tags",
          "type": "string[]",
          "required": false,
          "description": "Tags. They must be configured in the library."
        },
        {
          "name": "project_id",
          "type": "string (24-char hex id)",
          "required": false,
          "description": "Project to assign the email to."
        },
        {
          "name": "active_variation_id",
          "type": "string (24-char hex id)",
          "required": false,
          "description": "Variation to activate (libraries in variations mode `all` only). Opt-in: only one the user chose."
        },
        {
          "name": "url_tracking",
          "type": "object",
          "required": false,
          "description": "Campaign-level tracking values such as `{ \"utm_campaign\": \"spring-launch\" }`. Only when the read reports URL tracking enabled; values must satisfy the library's tracking rules."
        }
      ],
      "example": {
        "arguments": {
          "name": "Northwind Coffee – Autumn blend launch",
          "template_id": "64f0c0ffee0000000000abcd",
          "library_key": "northwind_marketing",
          "subject_line": "The Autumn blend is here",
          "preheader": "Roasted in small batches. Ships this week.",
          "content": [
            {
              "position": 1,
              "elements": [
                {
                  "position": 2,
                  "type": "text",
                  "text": "Meet the Autumn blend\nCaramel, toasted pecan and a clean finish."
                },
                {
                  "position": 3,
                  "type": "button",
                  "text": "Shop the blend",
                  "link": {
                    "url": "https://northwind.example/autumn"
                  }
                },
                {
                  "position": 1,
                  "type": "image",
                  "imageContent": {
                    "source_url": "https://northwind.example/img/autumn-hero.jpg",
                    "alt": "A bag of Autumn blend coffee beside a steaming mug on a wooden table"
                  }
                }
              ]
            }
          ]
        }
      },
      "responseDescription": "A report: the new draft's edit link, what was applied, every tenant warning, every skipped slot with its reason, images still missing, the new `save_version` and a rendered preview. If a later stage fails, the report still names the draft that was created so you continue from there instead of creating another."
    },
    {
      "name": "stensul-create-email",
      "group": "emails",
      "summary": "Creates an empty draft in a library from a list of module keys. Prefer `stensul-build-email` for normal creation.",
      "scope": "emails:write",
      "access": "write",
      "notes": "Use it when an empty structure is all you need. Under variations mode `all`, a request for several variations in one email cannot be expressed; the tool refuses rather than guessing. The result embeds a preview.",
      "params": [
        {
          "name": "library_key",
          "type": "string",
          "required": true,
          "description": "Key of the library to create the email in."
        },
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "Email name."
        },
        {
          "name": "modules",
          "type": "string[]",
          "required": false,
          "description": "Module keys from the library, in order (from `stensul-get-libraries`)."
        },
        {
          "name": "subject_line",
          "type": "string",
          "required": false,
          "description": "Subject line, when the library enables it."
        },
        {
          "name": "preheader",
          "type": "string",
          "required": false,
          "description": "Preheader, when the library enables it."
        },
        {
          "name": "title",
          "type": "string",
          "required": false,
          "description": "Title, when the library enables it."
        },
        {
          "name": "language",
          "type": "string",
          "required": false,
          "description": "Language code."
        },
        {
          "name": "tags",
          "type": "string[]",
          "required": false,
          "description": "Tags configured in the library."
        },
        {
          "name": "project_id",
          "type": "string (24-char hex id)",
          "required": false,
          "description": "Project to assign the email to."
        },
        {
          "name": "type",
          "type": "string",
          "required": false,
          "description": "Email type, for example `draft` or `template`."
        },
        {
          "name": "active_variation_id",
          "type": "string (24-char hex id)",
          "required": false,
          "description": "Variation to activate. Opt-in only."
        },
        {
          "name": "confirmed_repeated_modules",
          "type": "boolean",
          "required": false,
          "description": "Set `true` only after the user confirmed a repeated module key under variations mode `all`."
        },
        {
          "name": "include",
          "type": "string",
          "required": false,
          "description": "Include related resources in the response."
        }
      ],
      "example": {
        "arguments": {
          "library_key": "northwind_marketing",
          "name": "Northwind – empty newsletter shell",
          "modules": [
            "header",
            "hero",
            "two_column_cards",
            "footer"
          ]
        }
      },
      "responseDescription": "The new draft with an embedded preview. If images lack alt text, the result ends with an accessibility list to resolve in one follow-up call."
    },
    {
      "name": "stensul-copy-email",
      "group": "emails",
      "summary": "Creates an independent draft copy of an email, keeping all content, modules and layout. The original is never changed.",
      "scope": "emails:write",
      "access": "write",
      "notes": "Copying into a different library needs the `duplicate_email_between_libraries` permission. If the copy also needs new content, `stensul-build-email` with `copy_of` does both in one call.",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "ID of the email to copy."
        },
        {
          "name": "name",
          "type": "string",
          "required": true,
          "description": "Name for the copy."
        },
        {
          "name": "library_key",
          "type": "string",
          "required": false,
          "description": "Destination library (defaults to the source library)."
        },
        {
          "name": "language",
          "type": "string",
          "required": false,
          "description": "Language code for the copy."
        },
        {
          "name": "subject_line",
          "type": "string",
          "required": false,
          "description": "Subject line for the copy."
        },
        {
          "name": "preheader",
          "type": "string",
          "required": false,
          "description": "Preheader for the copy."
        },
        {
          "name": "title",
          "type": "string",
          "required": false,
          "description": "Title for the copy."
        },
        {
          "name": "tags",
          "type": "string[]",
          "required": false,
          "description": "Tags for the copy."
        },
        {
          "name": "project_id",
          "type": "string",
          "required": false,
          "description": "Project for the copy."
        },
        {
          "name": "type",
          "type": "string",
          "required": false,
          "description": "Email type for the copy."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd",
          "name": "Northwind – Autumn launch (DE)",
          "language": "de-DE"
        }
      },
      "responseDescription": "The new draft with an embedded preview. `created_by` may show as `System`; that is expected."
    },
    {
      "name": "stensul-update-email-content",
      "group": "emails",
      "summary": "Edits content inside an existing draft: copy, buttons, images, alt text, visibility, custom-element variables and URL tracking. Anything you leave out stays as it is.",
      "scope": "emails:write",
      "access": "write",
      "notes": "Address elements by module id plus element id from the read. Pass `save_version` from your last read so a stale edit fails with a conflict. Custom elements are written by variable value only; markup is never writable. Sent emails cannot be edited: copy them and edit the copy.",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "Email ID."
        },
        {
          "name": "modules",
          "type": "array",
          "required": false,
          "description": "`[{ id, style?, elements: [{ id, type, text?, content?, imageContent?, link?, style?, visibility?, variables?, url_tracking_parameters?, url_tracking_disabled? }] }]`. `type` is required on every element."
        },
        {
          "name": "save_version",
          "type": "integer",
          "required": false,
          "description": "The `save_version` from your last read. Always pass it when the read included one."
        },
        {
          "name": "url_tracking",
          "type": "object",
          "required": false,
          "description": "Campaign-level tracking values. A tracking-only fix can carry this with no `modules`."
        },
        {
          "name": "module",
          "type": "object",
          "required": false,
          "description": "Present in the manifest; use `modules`."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd",
          "save_version": 7,
          "modules": [
            {
              "id": 2817001,
              "elements": [
                {
                  "id": 3,
                  "type": "text",
                  "text": "Roasted this week. On your doorstep by Friday."
                },
                {
                  "id": 5,
                  "type": "button",
                  "text": "Order now",
                  "link": {
                    "url": "https://northwind.example/order"
                  }
                }
              ]
            }
          ]
        }
      },
      "responseDescription": "What was applied, tenant warnings, declined changes with reasons, the new `save_version` and a preview."
    },
    {
      "name": "stensul-update-email-settings",
      "group": "emails",
      "summary": "Changes a draft's name, title, subject line or preheader. The email body is untouched.",
      "scope": "emails:write",
      "access": "write",
      "notes": "Check the library first. Title, subject line and preheader are each enabled per library; a disabled field makes the tenant decline the whole call with a 422. A library may also cap lengths below 255.",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "Email ID."
        },
        {
          "name": "name",
          "type": "string (1–255)",
          "required": false,
          "description": "New name. Always allowed; cannot be cleared."
        },
        {
          "name": "subject_line",
          "type": "string (≤255)",
          "required": false,
          "description": "Only if the library enables it. Empty string clears it."
        },
        {
          "name": "preheader",
          "type": "string (≤255)",
          "required": false,
          "description": "Only if the library enables it. Empty string clears it."
        },
        {
          "name": "title",
          "type": "string (≤255)",
          "required": false,
          "description": "Only if the library enables it."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd",
          "subject_line": "Last call: Autumn blend ships Friday"
        }
      },
      "responseDescription": "Confirmation of the settings changed. No preview, since none of these fields render in the body."
    },
    {
      "name": "stensul-add-email-module",
      "group": "emails",
      "summary": "Adds one module (a whole section) from the library to a draft, found by what the user called it.",
      "scope": "emails:write",
      "access": "write",
      "notes": "Pass what the user called the block as `moduleName` and let the tool resolve it to a module in the library; never guess a module key. If no module matches, nothing is added. If several match, the tool returns a numbered question and a `moduleListToken`: ask the user, then call again with the same `moduleName`, the `moduleOrdinal` they picked and the token unchanged. Without `beforeModuleId` the new module lands as the last content block, above the library's fixed footer if it has one. To place it somewhere else, read the email first and pass the id of the module that sits in that spot now. Fixed headers and footers are governed by the library and can never be added. The result includes a preview of the updated email, so no separate preview call is needed.",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "Email ID."
        },
        {
          "name": "moduleName",
          "type": "string",
          "required": true,
          "description": "What the user called the block, in their own words (for example \"the testimonial block\"). The tool resolves it to a module. Ignored when `moduleKey` is sent."
        },
        {
          "name": "beforeModuleId",
          "type": "string | number",
          "required": false,
          "description": "Module instance id from `stensul-read-email` (not the module key) of the block that sits where the new one should go. The new module is inserted just before it. Omit to add it as the last content block."
        },
        {
          "name": "moduleOrdinal",
          "type": "integer",
          "required": false,
          "description": "The number the user picked from a previous disambiguation question. Send it with the same `moduleName` and `moduleListToken`."
        },
        {
          "name": "moduleListToken",
          "type": "string",
          "required": false,
          "description": "The token from the previous disambiguation question, passed back unchanged. If the library changed since, the tool asks again."
        },
        {
          "name": "moduleKey",
          "type": "string",
          "required": false,
          "description": "A module key already known from an earlier read. When present, name resolution is skipped."
        },
        {
          "name": "save_version",
          "type": "integer",
          "required": false,
          "description": "The email's version as you last read or wrote it. On a conflict nothing is added."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd",
          "moduleName": "the testimonial block",
          "beforeModuleId": "2817004",
          "save_version": 8
        }
      },
      "responseDescription": "The added module and the email's new `save_version`, with a preview of the updated email. Or a numbered question when several modules match the name, or a refusal that says why nothing was added."
    },
    {
      "name": "stensul-delete-email-module",
      "group": "emails",
      "summary": "Removes one module (a whole section) from a draft, or checks what else would go when the user asked to remove a single element.",
      "scope": "emails:write",
      "access": "write",
      "notes": "Set `removing` to what the user pointed at. `module` deletes that section in one call. `element` makes the tool check first: if the element can be hidden, hide it with `stensul-update-email-content` instead; if its section holds other content, the tool lists it and waits for the user's yes (`confirmedWithUser: true`).",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "Email ID."
        },
        {
          "name": "moduleId",
          "type": "string | number",
          "required": true,
          "description": "Module instance id from the read (not the module key)."
        },
        {
          "name": "removing",
          "type": "\"module\" | \"element\"",
          "required": true,
          "description": "What the user asked to remove."
        },
        {
          "name": "elementId",
          "type": "string | number",
          "required": false,
          "description": "Required when `removing` is `element`."
        },
        {
          "name": "confirmedWithUser",
          "type": "boolean",
          "required": false,
          "description": "Only after the tool listed what would be lost and the user agreed. Never on a first call."
        },
        {
          "name": "save_version",
          "type": "integer",
          "required": false,
          "description": "The email's version as you last read or wrote it."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd",
          "moduleId": 2817004,
          "removing": "module",
          "save_version": 8
        }
      },
      "responseDescription": "Confirmation of the deletion, or a refusal that explains why (for example a locked module) or lists what else the user must agree to lose."
    },
    {
      "name": "stensul-apply-email-variation",
      "group": "emails",
      "summary": "Applies a styling variation to the whole email, one module, or several modules in one call. Content is untouched and the change is reversible.",
      "scope": "emails:write",
      "access": "write",
      "notes": "The library's variations mode decides the shape. Mode `all`: one variation for the whole email, no `module_id`. Mode `individual`: per module, or `assignments` for several at once. The tool refuses what the mode can't express.",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "Email ID."
        },
        {
          "name": "variation",
          "type": "string",
          "required": false,
          "description": "Variation id, key or name. Required unless `assignments` is used."
        },
        {
          "name": "module_id",
          "type": "integer",
          "required": false,
          "description": "Module instance id. Required in mode `individual`; forbidden in mode `all`."
        },
        {
          "name": "assignments",
          "type": "array",
          "required": false,
          "description": "`[{ module_id, variation }]` for several modules in one write (mode `individual` only)."
        },
        {
          "name": "save_version",
          "type": "integer",
          "required": false,
          "description": "Version the change is based on."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd",
          "variation": "Dark"
        }
      },
      "responseDescription": "The restyled email with an embedded preview, or a refusal with the options the mode allows."
    },
    {
      "name": "stensul-list-gallery-images",
      "group": "images",
      "summary": "Browses the account's approved image gallery. Use it before asking the user for a file.",
      "scope": "dam:read",
      "access": "read",
      "notes": "`name` is a substring match on the image name, not a search of what the picture shows. Browse without it when unsure.",
      "params": [
        {
          "name": "name",
          "type": "string",
          "required": false,
          "description": "Part of the image name, case-insensitive."
        },
        {
          "name": "page_number",
          "type": "integer ≥ 1",
          "required": false,
          "description": "Page number (default 1)."
        },
        {
          "name": "page_size",
          "type": "integer 1–30",
          "required": false,
          "description": "Results per page (max 30, default 10)."
        },
        {
          "name": "sort",
          "type": "\"createdAt\" | \"-createdAt\" | \"updatedAt\" | \"-updatedAt\"",
          "required": false,
          "description": "Default `-createdAt` (newest first)."
        }
      ],
      "example": {
        "arguments": {
          "name": "autumn",
          "page_size": 10
        }
      },
      "responseDescription": "Gallery images with their `url`, which you can pass as `imageContent.source_url` in `stensul-build-email`."
    },
    {
      "name": "stensul-upload-images",
      "group": "images",
      "summary": "Uploads up to five images from public URLs to a draft (or to Studio for module backgrounds), or searches the gallery.",
      "scope": "images:write",
      "access": "write",
      "notes": "PNG, JPG, JPEG or GIF; up to 5 URLs per request and 3.5 MB per image. Gallery search also needs `dam:read`. For local or pasted files there is no URL; use `stensul-image-upload-session`.",
      "params": [
        {
          "name": "urls",
          "type": "string[] (≤5)",
          "required": false,
          "description": "Public http(s) image URLs ending in .png, .jpg, .jpeg or .gif."
        },
        {
          "name": "email_id",
          "type": "string (24-char hex id)",
          "required": false,
          "description": "Required when `target` is `email` and `urls` are passed."
        },
        {
          "name": "target",
          "type": "\"email\" | \"studio\"",
          "required": false,
          "description": "`email` (default) or `studio` for a module background image."
        },
        {
          "name": "gallery_search",
          "type": "string",
          "required": false,
          "description": "Search the gallery by image name instead of uploading. Empty string browses everything."
        },
        {
          "name": "page_number",
          "type": "integer ≥ 1",
          "required": false,
          "description": "Gallery search only."
        },
        {
          "name": "page_size",
          "type": "integer 1–30",
          "required": false,
          "description": "Gallery search only."
        }
      ],
      "example": {
        "arguments": {
          "email_id": "64f0c0ffee0000000000abcd",
          "urls": [
            "https://northwind.example/img/autumn-hero.jpg"
          ]
        }
      },
      "responseDescription": "Server-generated filenames to place with `stensul-update-email-content`, or `files[].path` for a Studio upload."
    },
    {
      "name": "stensul-image-upload-session",
      "group": "images",
      "summary": "The upload path for local images: files the user has, pastes or attaches. Mints a single-use browser upload link, or a small uploader script on hosts with a shell.",
      "scope": "images:write",
      "access": "write",
      "notes": "Links are valid for about 15 minutes and scoped to one email. Images are never base64-encoded into a tool call.",
      "params": [
        {
          "name": "action",
          "type": "\"start\" | \"check\"",
          "required": true,
          "description": "`start` mints the upload; `check` fetches the filenames after the user uploaded."
        },
        {
          "name": "email_id",
          "type": "string",
          "required": true,
          "description": "The email the images belong to."
        },
        {
          "name": "mode",
          "type": "\"link\" | \"script\"",
          "required": false,
          "description": "`link` (default, works on every host) or `script` (hosts with a shell, Python 3 and internet)."
        },
        {
          "name": "count",
          "type": "integer 1–5",
          "required": false,
          "description": "How many images the upload accepts (default 1)."
        },
        {
          "name": "labels",
          "type": "string[] (≤5)",
          "required": false,
          "description": "Per-slot labels shown on the upload page, for example `\"Hero banner (top)\"`."
        },
        {
          "name": "upload_id",
          "type": "string",
          "required": false,
          "description": "`check` only: the id returned by `start`."
        }
      ],
      "example": {
        "arguments": {
          "action": "start",
          "email_id": "64f0c0ffee0000000000abcd",
          "count": 1,
          "labels": [
            "Hero banner (top)"
          ]
        }
      },
      "responseDescription": "`start` (link mode): an upload link to give the user and an `upload_id`. `check`: the uploaded filenames."
    },
    {
      "name": "stensul-preview-email",
      "group": "quality",
      "summary": "Renders a read-only visual preview of an email, images included.",
      "scope": "emails:read",
      "access": "read",
      "notes": "Falls back so every host shows something: HTML in an MCP App frame, then a PNG, then a link to the editor. Per the help center, the preview renders in the chat in Claude and ChatGPT, comes back as a static image in Writer, and opens through the edit link in Copilot.",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "Email ID to preview."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd"
        }
      },
      "responseDescription": "An in-chat rendering of the email, or a PNG, or an editor link, depending on what the host supports."
    },
    {
      "name": "stensul-run-quality-check",
      "group": "quality",
      "summary": "Runs the account's accessibility and quality checks on an email and returns the issues found.",
      "scope": "emails:read",
      "access": "read",
      "notes": "Each issue has a readable `message`, a `rule`, and for element-level issues a `locationText` such as \"the 2nd image in the 3rd module\". An empty `errors` array means the email passed every active check.",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "Email to check."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd"
        }
      },
      "responseDescription": "`errors`: a list of `{ message, rule, locationText? }`. Empty when everything passes."
    },
    {
      "name": "stensul-list-segment-sets",
      "group": "dynamic",
      "summary": "Lists the Segment Sets available for a library: each set's name, segment type and ordered segments.",
      "scope": "dynamic-content:read",
      "access": "read",
      "notes": "Check `dca_segment_sets.supported` in `stensul-get-libraries` first. Not every library supports Segment Sets.",
      "params": [
        {
          "name": "libraryId",
          "type": "string",
          "required": true,
          "description": "Library ID."
        }
      ],
      "example": {
        "arguments": {
          "libraryId": "64f0c0ffee0000000000beef"
        }
      },
      "responseDescription": "Segment Sets with their segments in order."
    },
    {
      "name": "stensul-apply-segment-set",
      "group": "dynamic",
      "summary": "Applies an existing Segment Set to a module (or one element) and can write each segment's content in the same call.",
      "scope": "dynamic-content:write",
      "access": "write",
      "notes": "Segments are matched by name or id. Segments you leave out keep the seed content; write them later with `stensul-update-segment-content`. The audiences and their logic are defined by an admin in Stensul and can't be changed here.",
      "params": [
        {
          "name": "emailId",
          "type": "string (24-char hex id)",
          "required": true,
          "description": "Email ID."
        },
        {
          "name": "libraryId",
          "type": "string",
          "required": false,
          "description": "Library the email belongs to."
        },
        {
          "name": "moduleInstanceId",
          "type": "integer",
          "required": true,
          "description": "Module instance to make dynamic."
        },
        {
          "name": "segmentSetId",
          "type": "string",
          "required": true,
          "description": "Segment Set to apply."
        },
        {
          "name": "elementInstanceId",
          "type": "integer",
          "required": false,
          "description": "One element instead of the whole module."
        },
        {
          "name": "segments",
          "type": "array",
          "required": false,
          "description": "`[{ segment, isHiddenSegment?, elements?: [{ elementInstanceId, elementType, content?, imageContent?, link?, style?, visibility? }] }]`."
        }
      ],
      "example": {
        "arguments": {
          "emailId": "64f0c0ffee0000000000abcd",
          "moduleInstanceId": 2817002,
          "segmentSetId": "seg_loyalty_tier",
          "segments": [
            {
              "segment": "Gold",
              "elements": [
                {
                  "elementInstanceId": 3,
                  "elementType": "text",
                  "content": [
                    {
                      "type": "paragraph",
                      "content": [
                        {
                          "text": {
                            "content": "Gold members get free shipping on the Autumn blend."
                          }
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            {
              "segment": "Everyone else",
              "elements": [
                {
                  "elementInstanceId": 3,
                  "elementType": "text",
                  "content": [
                    {
                      "type": "paragraph",
                      "content": [
                        {
                          "text": {
                            "content": "Free shipping on orders over $40."
                          }
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      },
      "responseDescription": "The resulting segments (`dynamicElementId`, `segmentId`, `segmentName`) and, per written segment, what was applied and any template warnings."
    },
    {
      "name": "stensul-update-segment-content",
      "group": "dynamic",
      "summary": "Writes the content of one segment created by `stensul-apply-segment-set`. All of that segment's elements go in one call.",
      "scope": "dynamic-content:write",
      "access": "write",
      "notes": "Same content shapes as `stensul-update-email-content`. Only styling the template allows is applied; the rest is dropped and reported under `warnings`.",
      "params": [
        {
          "name": "dynamicElementId",
          "type": "string",
          "required": true,
          "description": "Dynamic element ID returned by `stensul-apply-segment-set`."
        },
        {
          "name": "segmentId",
          "type": "string",
          "required": true,
          "description": "Segment to write."
        },
        {
          "name": "elements",
          "type": "array (≥1)",
          "required": true,
          "description": "`[{ elementInstanceId, elementType, content?, imageContent?, link?, style?, visibility? }]`."
        },
        {
          "name": "isHiddenSegment",
          "type": "boolean",
          "required": false,
          "description": "Hide this whole segment. Omit to keep its current visibility."
        }
      ],
      "example": {
        "arguments": {
          "dynamicElementId": "dyn_01",
          "segmentId": "seg_gold",
          "elements": [
            {
              "elementInstanceId": 5,
              "elementType": "button",
              "content": [
                {
                  "content": [
                    {
                      "text": {
                        "content": "Claim free shipping"
                      }
                    }
                  ]
                }
              ],
              "link": {
                "url": "https://northwind.example/gold"
              }
            }
          ]
        }
      },
      "responseDescription": "The updated segment, with warnings for anything the template didn't allow."
    }
  ],
  "errors": [
    {
      "status": "401",
      "message": "Authentication expired or missing",
      "cause": "The session's authorization expired.",
      "fix": "Sign in again when the host prompts. On a 401 or 403, the server's instructions tell the model to re-check sign-in first."
    },
    {
      "status": "403",
      "message": "Insufficient OAuth scope (403): The token lacks the required scope",
      "cause": "The API connection doesn't have the scope this tool needs.",
      "fix": "An admin adds the scope to the API connection. Then sign in again: adding a scope revokes existing tokens."
    },
    {
      "status": "403",
      "message": "Invalid scope(s) provided.",
      "cause": "The connection was created with only `api-read`/`api-write`, which apply to server-to-server integrations and do nothing for MCP.",
      "fix": "Select the granular scopes (`emails:read`, `emails:write`, `libraries:read`, …) on the connection."
    },
    {
      "status": "403",
      "message": "API error (403): Action is unauthorized.",
      "cause": "The signed-in user's role lacks the permission, for example `edit_campaign`.",
      "fix": "Grant the permission to the user's role in Admin > Users & Roles. It works for other users on the same connection."
    },
    {
      "status": "403",
      "message": "API error (403): Sent emails cannot be edited.",
      "cause": "The email has already been sent.",
      "fix": "Nothing to fix. Copy it and edit the copy."
    },
    {
      "status": "403",
      "message": "Access denied (403): Your IP address is not in the allowed list",
      "cause": "The caller's IP isn't in the external API allowlist.",
      "fix": "An admin adds the IP in Settings > External API > Allowed IPs."
    },
    {
      "status": "403",
      "message": "API error (403):",
      "cause": "The server didn't say which check failed.",
      "fix": "Work through the other 403 rows: confirm sign-in with `stensul-check-auth`, then scopes on the connection, the person's permissions, and the IP allowlist."
    },
    {
      "status": "409",
      "message": "Version conflict",
      "cause": "The email changed since your last read, and you passed its old `save_version`.",
      "fix": "Read the email again and re-apply your change on the current version. Never retry by dropping `save_version`."
    },
    {
      "status": "422",
      "message": "Field not enabled for this library",
      "cause": "`stensul-update-email-settings` sent a title, subject line or preheader the library doesn't enable, so the whole call is declined.",
      "fix": "Send only fields the library enables (see `stensul-get-libraries`)."
    },
    {
      "status": "declined",
      "message": "Content declined by a guardrail",
      "cause": "A value is outside what the template allows: an off-palette color, a size out of range, locked text.",
      "fix": "Keep the value within the guardrail or make no change. Guardrails are enforced by Stensul and cannot be overridden from a chat."
    },
    {
      "status": "sign-in",
      "message": "Redirect URI mismatch",
      "cause": "The redirect URI on the API connection is missing your instance name or doesn't match exactly.",
      "fix": "Set it to exactly `https://mcp.stensul.com/<your-instance>/oauth/callback` (Writer also needs its own callback)."
    },
    {
      "status": "410",
      "message": "Gone",
      "cause": "A client is still calling the old shared `/mcp` endpoint with `X-Stensul-*` headers, which was removed.",
      "fix": "Use the per-instance endpoint `https://mcp.stensul.com/<your-instance>/mcp` with OAuth. Existing Client IDs keep working."
    },
    {
      "status": "422",
      "message": "Fixed header or footer can't be added",
      "cause": "`stensul-add-email-module` was asked for a module the library fixes at the top or bottom of every email.",
      "fix": "Nothing to fix. Those modules are governed by the library. Report it and don't offer a substitute."
    },
    {
      "status": "422",
      "message": "The \"before\" module is not in this email",
      "cause": "The email changed since it was read, so the module named in `beforeModuleId` is gone.",
      "fix": "Read the email again and ask the user where the new module should go now."
    }
  ]
}
