> ## Documentation Index
> Fetch the complete documentation index at: https://docs.consile.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Write tools & guardrails

> How write-capable connectors (e-conomic, Shopify, HubSpot CMS, HubSpot Marketing Email, LinkedIn Ads, Meta Ads) behave: the dry-run preview shape, the confirm:true argument, DRAFT/PAUSED defaults, and the honest caveat that guardrails are connector-local application logic: not a core platform gate.

Several Consile connectors are **read-only**: Google Search Console, Uniconta and
Dinero expose **only** read tools, so the AI can read your numbers but can never
change, create or delete anything upstream. Other connectors are
**read + guardrailed write**: alongside their read tools they can create and manage
content, behind explicit safety defaults. Those are **e-conomic**, **Shopify**,
**HubSpot CMS**, **HubSpot Marketing Email**, **LinkedIn Ads** and **Meta Ads**.

This page documents the developer-facing mechanics of those write tools: the
default-status behaviour, the `confirm` argument, and the dry-run preview
shape. For the plain-language safety model, see
[Handlinger & guardrails](/essentials/handlinger-og-guardrails).

<Info>
  Each connector is its own named MCP at `https://mcp.consile.ai/<id>/mcp` with
  only its own tools. Tool calls go to the **connector** endpoint: the aggregate
  `https://mcp.consile.ai/mcp` is the Consile Concierge (management tools only),
  not where write tools are called. The transport namespaces every tool as
  `<id>__<tool>` (e.g. `economic__create_draft_invoice`,
  `shopify__set_product_status`, `linkedin-ads__set_campaign_status`). The AI
  invokes these names on your behalf.
</Info>

## Which connectors have write tools

| Connector                   | Endpoint                           | Capability                   | Write tools |
| --------------------------- | ---------------------------------- | ---------------------------- | ----------- |
| Google Search Console       | `mcp.consile.ai/gsc/mcp`           | read-only                    | 0           |
| Uniconta                    | `mcp.consile.ai/uniconta/mcp`      | read-only                    | 0           |
| Dinero                      | `mcp.consile.ai/dinero/mcp`        | read-only                    | 0           |
| **e-conomic**               | `mcp.consile.ai/economic/mcp`      | **read + guardrailed write** | 4 of 27     |
| **Shopify**                 | `mcp.consile.ai/shopify/mcp`       | **read + guardrailed write** | 10 of 50    |
| **HubSpot CMS**             | `mcp.consile.ai/hubspot/mcp`       | **read + guardrailed write** | 9 of 23     |
| **HubSpot Marketing Email** | `mcp.consile.ai/hubspot-email/mcp` | **read + guardrailed write** | 11 of 19    |
| **LinkedIn Ads**            | `mcp.consile.ai/linkedin-ads/mcp`  | **read + guardrailed write** | 19 of 64    |
| **Meta Ads**                | `mcp.consile.ai/meta-ads/mcp`      | **read + guardrailed write** | 5 of 39     |

The read-only connectors have **zero** write tools; every tool on those endpoints is a read. (The upcoming CVR and Billy connectors are read-only too.) Everything below applies only to the write-capable connectors.

<Note>
  **Meta Ads** is live and served on its own endpoint, but shows as **coming
  soon** in the portal until self-serve purchase is provisioned.
</Note>

***

## The three guardrail rules

Every write tool on the write-capable connectors obeys the same three rules:

1. **New objects are created paused / draft.** A new object is never live: Meta
   ads are created **PAUSED**; new LinkedIn, Shopify and HubSpot objects are
   **DRAFT**; an e-conomic draft invoice is a draft; a new HubSpot marketing email
   can never send. It spends nothing and does nothing until you explicitly take it
   live.
2. **Money / destructive / public actions require `confirm: true`.** Anything that
   spends money (setting a campaign `ACTIVE`), changes a budget or bid, changes a
   price or inventory, publishes to a live website, sends a marketing email, or
   archives / deletes an object requires an explicit `confirm: true` argument.
3. **Without `confirm`, you get a dry-run.** A guarded tool called without
   `confirm: true` returns a preview object describing exactly what *would*
   happen and calls **no** upstream API. Nothing changes until you re-issue the
   call with `confirm: true`.

<Warning>
  These guardrails are **connector-local application logic**: designed-in safety
  defaults enforced inside each connector's tool handlers. The platform's
  `readOnly` flag on a tool is a **hint only** (it surfaces as
  `annotations.readOnlyHint` in the manifest); the core and transport will happily
  run a *mutating* handler. There is **no separate core destructive-op gate** that
  would stop a write tool independently of the connector's own logic. So this is a
  strong safety default, not an unbreakable platform guarantee. Read tools, by
  contrast, are safe by construction: a read-only connector ships no write tools at
  all.
</Warning>

***

## Default status: DRAFT / PAUSED

Creating an object never makes it live:

* **Meta Ads**: `create_campaign`, `create_ad_set` and `create_ad` create the
  object with status **PAUSED**. It exists, but does not deliver and spends
  nothing.
* **LinkedIn Ads**: the create tools produce the object in **DRAFT** status.
* **Shopify**: `create_product` creates the product as **DRAFT** (not published to
  any sales channel).
* **HubSpot CMS**: new pages / blog posts are forced to **DRAFT** on create; HubDB
  row writes land in the table's draft.
* **HubSpot Marketing Email**: new emails are forced to state **DRAFT** and can
  never be sent or scheduled by create/update.
* **e-conomic**: `create_draft_invoice` creates a draft invoice; every write tool
  is preview-first (see below).

Taking an object live (going `ACTIVE`, publishing, sending, or changing price /
inventory) requires the relevant tool **and** `confirm: true`, because those steps
spend money or affect the live surface (rule 2).

***

## The `confirm` argument

Guarded tools accept a boolean `confirm` argument.

<ParamField path="confirm" type="boolean" default="false">
  Set `true` to actually execute the mutation upstream. When omitted or `false`,
  the tool performs **no** upstream call and returns a dry-run preview
  instead.
</ParamField>

A call is **guarded** (needs `confirm: true` to execute) when it would:

* **spend money**: set a campaign's status to `ACTIVE` (go live);
* **change a budget or bid**: e.g. `update_budget` on Meta Ads;
* **change a price or inventory**: e.g. on Shopify;
* **publish to a live surface**: a live website (HubSpot CMS), a marketing email
  (`publish_email`), or public organic-social content (LinkedIn);
* **archive or delete** an object.

Creating a paused / draft object is itself low-risk, but going live is the guarded
step. For **e-conomic**, every write tool (including the create tools) is
preview-first: `requiresConfirm()` is always `true`.

***

## The dry-run preview shape

When a guarded tool is called without `confirm: true`, it returns a structured
preview of the intended change and makes no upstream request. The exact field set
varies by connector, but the shape is consistent:

```json Dry-run preview (no confirm) theme={null}
{
  "preview": true,
  "action": "update_status",
  "connector": "meta-ads",
  "wouldChange": {
    "objectType": "campaign",
    "objectId": "1203456789012345",
    "from": { "status": "PAUSED" },
    "to": { "status": "ACTIVE" }
  },
  "requiresConfirm": true,
  "message": "Dry-run only. Re-issue this call with confirm: true to apply."
}
```

Key fields:

<ResponseField name="preview" type="boolean">
  `true` whenever this is a dry-run: no upstream API was called.
</ResponseField>

<ResponseField name="action" type="string">
  The write tool that produced the preview (e.g. `update_status`, `publish_email`).
</ResponseField>

<ResponseField name="wouldChange" type="object">
  The intended mutation: object type, id, and the field deltas that would be
  written if confirmed.
</ResponseField>

<ResponseField name="requiresConfirm" type="boolean">
  `true` for money / destructive / public actions; re-issue with `confirm: true`
  to apply.
</ResponseField>

To apply the change, the AI re-issues the **same** call with `confirm: true`:

```json Confirmed write (executes upstream) theme={null}
{
  "name": "update_status",
  "arguments": {
    "campaignId": "1203456789012345",
    "status": "ACTIVE",
    "confirm": true
  }
}
```

<Note>
  The AI runs these calls for you. It is expected to show you the preview
  and get your go-ahead before re-issuing with `confirm: true`. Treat the
  preview as the moment to review before any spend, publish or send.
</Note>

***

## e-conomic write tools

e-conomic exposes **4 write tools** (of 27 total): `create_customer`,
`update_customer`, `create_draft_invoice`, `duplicate_invoice`.

* **All four are preview-first.** The first call never hits the e-conomic API; it
  returns a dry-run of the exact request (method, path, body). The change is
  applied only on a second call with `confirm: true`. `requiresConfirm()` is always
  `true`.
* **No ledger posting and no deletion.** The connector deliberately cannot post a
  draft to the ledger and has no DELETE method at all. A draft invoice is a real
  artifact the bookkeeper sees, but it does not affect the accounts until it is
  posted manually in e-conomic.
* `update_customer` is a **full-record PUT** (e-conomic has no PATCH): it reads the
  current customer, merges only the fields you supply, and strips server-owned
  fields. `duplicate_invoice` reads the source, strips id / amount / posting fields
  and creates a fresh draft.

***

## Shopify write tools

Shopify exposes **10 write tools** (of 50 total), limited to the three granted
write scopes: products, publication to sales channels, and inventory.

* Orders, draft orders, customers and discounts are **read-only**, and
  `graphql_query` rejects mutations.
* New products are created **DRAFT**. Going live (publishing or status `ACTIVE`),
  archiving, changing a price, changing inventory or deleting requires
  `confirm: true`; the first call without confirm is a dry-run preview.
* Mutation responses are checked for `userErrors`, so a rejected write never looks
  like a success.

***

## HubSpot CMS write tools

HubSpot CMS exposes **9 write tools** (of 23 total).

* **Draft writes run freely**: `create_page_draft`, `update_page_draft`,
  `create_blog_post_draft`, `update_blog_post_draft`, `upsert_hubdb_row_draft`. A
  draft never touches the live website.
* **Actions that hit the live site or discard work require `confirm: true`**:
  `publish_page`, `publish_blog_post`, `publish_hubdb_table`, `reset_hubdb_draft`.
  The first call without confirm returns a structured preview and calls no HubSpot
  API.
* HubDB row writes land in the table's **draft** and stay invisible on live pages
  until `publish_hubdb_table` runs. **No hard deletes** in v1.

***

## HubSpot Marketing Email write tools

HubSpot Marketing Email exposes **11 write tools** (of 19 total).

* **Free, reversible draft writes** (4): `create_email`, `update_email_draft`,
  `clone_email`, `create_email_ab_variation`. They only work in draft and can never
  send; new emails are forced to state DRAFT, and create/update never accept
  `state`, `publishDate` or `sendOnPublish`.
* **Confirm-gated writes** (7): `update_email` (mutates the live version),
  `publish_email` (sends a real email, highest risk), `unpublish_email`,
  `delete_email`, `reset_email_draft`, `restore_email_revision`,
  `restore_email_revision_to_draft`. Without `confirm: true` they return a dry-run
  preview; the second call with `confirm: true` executes. Sending to real
  recipients is possible **only** via the explicitly confirmed `publish_email`.

***

## Meta Ads write tools

Meta Ads exposes **5 write tools** (of 39 total):

| Tool              | Guarded?        | Notes                                                  |
| ----------------- | --------------- | ------------------------------------------------------ |
| `create_campaign` | create → PAUSED | New campaign, never live until activated.              |
| `create_ad_set`   | create → PAUSED |                                                        |
| `create_ad`       | create → PAUSED | Ads are built only from an existing creative.          |
| `update_status`   | **confirm**     | Going `ACTIVE` spends money; archiving is destructive. |
| `update_budget`   | **confirm**     | Budget/bid changes always require confirm.             |

<Warning>
  Meta Ads has **no hard-delete**: there is no delete tool. Objects can only be
  **archived** (via `update_status`), which is itself guarded by `confirm: true`.
</Warning>

***

## LinkedIn Ads write tools

LinkedIn Ads exposes **19 write tools** (of 64 total): **11 ad-management** tools
plus **8 organic-social** tools.

* **Ad-management writes** follow the standard model: new objects are created in
  **DRAFT**; activation, budget/bid changes, and archive/delete require
  `confirm: true`, otherwise a dry-run preview is returned. (Measurement writes such
  as creating/updating a conversion rule apply without confirm.)
* **Organic-social writes** publish public content, so they are confirm-gated by
  the same rules.

<Warning>
  The **8 organic-social write tools** are exposed but currently **fail closed**:
  until LinkedIn grants Community Management API approval, calling them returns
  **HTTP 403**. Treat them as **"requires approval / not active yet"**, as they are
  listed but not usable today. The 45 read tools and the 11 ad-management write
  tools are unaffected.
</Warning>

***

## Why read-only connectors are different

To restate the boundary precisely: **Google Search Console, Uniconta and Dinero
ship no write tools at all** (and so will the upcoming CVR and Billy connectors).
Their safety is structural, not behavioural: there is no mutating handler to gate.
For example, the Uniconta balance tool `uniconta__get_account_balance` only reads a
balance; there is no Uniconta tool that posts an entry or edits a record.

The guardrail machinery on this page exists specifically because the write-capable
connectors *can* write. For the full plain-language model, see
[Handlinger & guardrails](/essentials/handlinger-og-guardrails).

<Note>
  A failed or rejected upstream write surfaces as a normal tool error (e.g. a
  `400`/`403` from the provider, or LinkedIn's `403` for un-approved
  organic-social tools). See [Errors & limits](/developers/errors-and-limits).
</Note>
