openapi: 3.1.0
info:
  title: Nucleo MCP Connector API
  version: "2026-10"
  summary: One remote MCP server that gives Claude, ChatGPT and other AI clients the Nucleo tools of a user.
  description: |
    **Server URL: `https://mcp.nucleoplatform.com`**

    The Nucleo connector is a remote [Model Context Protocol](https://modelcontextprotocol.io) server
    (protocol version `2025-06-18`, Streamable HTTP transport, JSON responses). Add it as a custom
    connector in your AI client and sign in with your Nucleo account: the client then sees the tools of
    every Nucleo module you can use in the active company — Brain, Catalog, Commerce, Intelligence —
    with your own permissions. Delete tools are not exposed, with one exception: `catalog_forget_memory`
    deletes an entry of Atomo's memory in Catalog. A few writes cannot be reversed automatically — that one,
    merging duplicate contacts or tasks in Brain, and website CMS tools that the CMS marks as destructive —
    and carry `destructiveHint: true`, so your AI client can ask you before running them.

    Authentication is OAuth 2.1 with discovery: the server answers `401` with a pointer to its
    protected-resource metadata (RFC 9728), which names Nucleo Core as the authorization server; the
    client registers itself (RFC 7591), runs the authorization code flow with PKCE and sends
    `Authorization: Bearer <token>` on every request. See the *OAuth & OpenID Connect* API.

    **Connect Claude** (web, desktop, mobile): Settings → Connectors → *Add custom connector* → name
    `Nucleo`, URL `https://mcp.nucleoplatform.com` → *Connect* and sign in.
    **Connect ChatGPT** (developer mode): Settings → Connectors → *Advanced* → enable developer mode →
    *Create* → MCP server URL `https://mcp.nucleoplatform.com`, authentication OAuth → sign in.
x-nucleo:
  product: mcp
  module: platform
  audience: [ai-assistant]
  stability: beta
  format: json
  order: 2
servers:
  - url: https://mcp.nucleoplatform.com
    description: Production. The root URL is the MCP endpoint.
security:
  - nucleoOAuth: []
tags:
  - name: Discovery
    description: OAuth protected-resource metadata (RFC 9728) that MCP clients read after a `401`.
  - name: MCP
    description: |
      The MCP endpoint. One JSON-RPC 2.0 request per HTTP `POST`, one JSON response.

      **Methods**: `initialize`, `ping`, `tools/list`, `tools/call`. Notifications (`notifications/*`,
      no `id`) are accepted with `202`. Anything else → JSON-RPC error `-32601`.
      Not supported in this version: server-initiated streams (`GET` → `405`), SSE responses, JSON-RPC
      batches, resources, prompts, sampling. The server is stateless: no `Mcp-Session-Id`.

      **Active company.** Every module tool runs inside one company. At `initialize` the connector picks
      it in this order: your only company → the company you chose earlier with `nucleo_switch_company`
      → the first company. The `initialize` result's `instructions` start with
      `ACTIVE COMPANY: "<name>"` and the modules available there. Switching company is saved for you in
      Nucleo and applies to every AI client you connect.

      **Tool names** carry the module prefix: `brain_`, `catalog_`, `commerce_`, `intelligence_`; the two
      connector tools start with `nucleo_`. A module's tools appear only if the company has the module,
      the company has not switched it off for AI clients, it is set up, and you have access to it.
      Definitions are cached for 60 seconds per person, company and module.

      **Annotations.** Every tool carries the MCP `annotations` its module declares, passed through unchanged:
      `readOnlyHint` (it only reads), `destructiveHint` (a write that cannot be reversed automatically, such
      as merging duplicates in Brain), `idempotentHint` (true for reads) and `openWorldHint` (it reaches people
      outside Nucleo, such as sending an email or a Slack message). A hint the module leaves out is derived
      from whether the tool reads or writes, and a write tool is never presented as read-only. In the tool
      tables below, *Access* shows them: `read`, `write`, `write, destructive`, `write, open world`.

      **Errors.**
      - Tool-level problems come back as a normal result with `isError: true` and a readable text: no
        permission (`You do not have permission ... for this action.`), invalid arguments, module not
        available in the company (with a hint to switch company), module unreachable or failing
        (`Nucleo <Module> is not reachable right now (request <id>)` — never internal details).
      - JSON-RPC errors: `-32700` parse error (HTTP 400), `-32601` method not found, `-32602` unknown
        tool, `-32603` internal error, `-32000` module rate-limited (`... busy, retry in N seconds`).

      **Limits.** Request body up to 2 MB. Module calls time out after 30 seconds. Modules rate-limit
      each person to 120 tool calls and 120 tool lists per minute (Catalog, Commerce, Intelligence).
      Token validation is cached for up to 60 seconds, but a token revoked in Nucleo (sign-out, disconnect,
      refresh) is refused immediately.

      **Privacy.** Nucleo records which client you used, which tool, outcome and duration for the
      *AI clients* overview — never arguments or results.
  - name: "Tools · Nucleo (gateway)"
    description: |
      Always available. Company selection for every module tool.

      2 tools.

      | Tool | Access | Requires | What it does | Parameters |
      |---|---|---|---|---|
      | `nucleo_list_companies` | read | — | List the companies you can work in, the modules available in each through the connector, and which one is active. | (none) |
      | `nucleo_switch_company` | write | — | Make another company the active one, for this and the next sessions; every module tool then works inside it. | company_id:integer (required, from nucleo_list_companies) |
  - name: "Tools · Brain"
    description: |
      CRM, inbox and timeline, Knowledge and playbooks, sales documents, tasks, saved AI conversations, GDPR requests (read-only) and generic records. Visible tools depend on the person's Brain permissions.

      64 tools (31 read, 33 write: 2 destructive, 2 that reach people outside Nucleo), plus the tools of the
      connected website CMS. Which ones you see depends on your Brain permissions (*Requires*); a tool with
      `—` is visible to every Brain user.

      | Tool | Access | Requires | What it does | Parameters |
      |---|---|---|---|---|
      | `brain_add_contact_email` | write | contacts.edit | Add an alias email to an existing contact without changing the primary email. | contact_id:integer (required), email:string (required) |
      | `brain_add_note` | write | contacts.edit | Add a note to a contact, company or deal. | entity:string (required; contact, company, deal), entity_id:integer (required), subject:string, body:string (required) |
      | `brain_attach_email_attachment_to_deal` | write | deals.edit | Attach an Inbox email attachment to a deal as its contract or as a deal document. | inbox_email_id:integer (required), deal_id:integer (required), as:string (contract, document; default contract), filenames:string[], signed:boolean, mark_won:boolean, doc_type:string |
      | `brain_create_company` | write | companies.edit | Create a CRM company. | name:string (required), vat_number, main_domain, website, phone, sector, lifecyclestage, address_city, address_country:string |
      | `brain_create_contact` | write | contacts.edit | Create a CRM contact. | first_name:string (required), last_name:string (required), email, phone, mobile:string, company_id:integer, lifecyclestage:string |
      | `brain_create_deal` | write | deals.create | Create a deal, optionally from an email or a suggestion; warns about existing open deals. | title:string (required), company_id:integer, company_name:string, create_company:boolean, contact_id:integer, stage:string, value_eur:number, expected_close_date:string, note:string, inbox_email_id:integer, suggestion_id:integer, confirm_new:boolean |
      | `brain_create_deal_document` | write | deals.edit | Save an AI-written deal document (brief, assessment, quote, contract…) as a numbered HTML draft. | deal_id:integer (required), title:string (required), doc_type:string (required), html:string (required), format:string (a4, deck), locale:string (it, en), valid_until:string, document_template_id:integer, document_id:integer, contract:object, document_number:string |
      | `brain_create_document_template` | write | templates.manage | Create a document template as a draft, mirroring an existing template's structure. | kind:string (required; assessment, quote, contract, order, tc), code:string (max 12), family:string, variant:string, name:object (required, en/it), description:object, layout:object, blocks:object[] (required), rules:object, parameters:object, notes:string |
      | `brain_create_message_draft` | write | — | Create an email or Slack message draft (nothing is sent), new or reply-all, with attachments. | channel:string (email, slack), inbox_email_id:integer, to:string[], cc:string[], subject:string, body:string (required), reply_to_message_id:integer, slack_to:string, attachments:object[] |
      | `brain_create_record` | write | per record type | Create a generic Brain record of a given type (see list_record_types). | type:string (required), fields:object (required) |
      | `brain_create_task` | write | — | Create a task (reuses an identical open one), optionally assigned and in a project. | title:string (required), description:string, due_date:string, assignee:string, project_id:integer, suggestion_id:integer |
      | `brain_get_chat_message` | read | — | Read a received Slack message with its recent conversation. | message_id:integer (required) |
      | `brain_get_company` | read | companies.view | Company details with linked contacts and active deals. | id:integer (required) |
      | `brain_get_company_brief` | read | — | One-call company summary: relationship, contacts, deals, contracts, calls, saved AI conversations. | company_id:integer, name:string |
      | `brain_get_company_timeline` | read | companies.view | Recent company context: deals, timeline activities and saved AI conversations. | company_id:integer (required), days:integer (7–730, default 180), limit:integer (5–50, default 20) |
      | `brain_get_contact` | read | contacts.view | Contact details with company and recent deals. | id:integer (required) |
      | `brain_get_contract` | read | contracts.view | Contract status, dates, renewal, notice, amount, withdrawal and linked quotes. | contract_id:integer (required) |
      | `brain_get_deal` | read | deals.view | Deal details: company, contacts, owner, stage, services. | id:integer (required) |
      | `brain_get_document_template` | read | templates.view | Read a document template (layout, blocks, rules, parameters) by id or code. | id:integer, code:string |
      | `brain_get_email` | read | — | Read an Inbox email with body, attachments, thread and linked company/deal. | inbox_email_id:integer (required) |
      | `brain_get_knowledge_proposal` | read | area owner | Read a pending Knowledge change proposal with the current page text. | proposal_id:integer (required) |
      | `brain_get_playbook` | read | — | Read the Knowledge playbook or procedure on a topic, or a specific page. | topic:string, page_id:integer, slug:string, limit:integer (1–5, default 2) |
      | `brain_get_privacy_request` | read | gdpr.manage | Read a GDPR request by id or reference, with its event log. | id:integer, reference:string, events_limit:integer (1–100, default 30) |
      | `brain_get_quote` | read | quotes.view | Read a quote with status, totals and line items. | quote_id:integer (required) |
      | `brain_get_record` | read | per record type | Read one generic record with fields, links, related records and URL. | type:string (required), id:integer (required) |
      | `brain_list_calendar_events` | read | — | List your own Google Calendar events in a date range (max 62 days). | from:string (date, default today), to:string (date), query:string |
      | `brain_list_deal_documents` | read | deals.view | List a deal's documents with number, status, language, validity and versions. | deal_id:integer (required), doc_type:string |
      | `brain_list_document_templates` | read | templates.view | List active document templates with code, variant and parameters. | kind:string (assessment, quote, contract, order, tc), include_archived:boolean |
      | `brain_list_message_drafts` | read | — | List your open email and Slack drafts. | limit:integer (default 15, max 30) |
      | `brain_list_my_tasks` | read | — | List your open tasks, soonest due first. | limit:integer (1–50, default 20), overdue_only:boolean |
      | `brain_list_privacy_requests` | read | gdpr.manage | List GDPR requests with status, deadline and assignee. | status:string (default open), type:string, search:string, overdue_only:boolean, limit:integer (1–50, default 20) |
      | `brain_list_record_types` | read | — | Manual of the generic record types and fields you can use. | type:string |
      | `brain_list_tasks` | read | — | List open tasks (yours, a colleague's or a project's) by urgency. | assignee:string, project_id:integer, overdue_only:boolean, due_within_days:integer, limit:integer (default 15, max 50) |
      | `brain_log_note` | write | — | Add a note to a contact, company or deal timeline. | body:string (required), subject:string, contact_id:integer, company_id:integer, deal_id:integer |
      | `brain_merge_contacts` | write, destructive | contacts.edit | Merge a duplicate contact into the primary one after explicit confirmation (the duplicate goes to the trash). | primary_contact_id:integer (required), secondary_contact_id:integer (required), confirmed_by_user:boolean (required) |
      | `brain_merge_tasks` | write, destructive | tasks.manage | Find similar duplicate tasks, or merge them into one after confirmation. | task_id:integer, list_only:boolean, keep_id:integer, merge_ids:integer[], confirmed_by_user:boolean |
      | `brain_propose_knowledge_page` | write | — | Propose a new or updated Knowledge page for the area owner to approve. | area_slug:string (required), title:string (required), body_md:string (required), kind:string, summary:string, rationale:string (required), page_id:integer |
      | `brain_read_drive_file` | read | — | Open a Google Drive/Docs file by link or id and return its text. | url:string (required), offset:integer |
      | `brain_read_email_attachment` | read | inbox.view | Read the text of an Inbox email attachment (PDF, Word, images). | inbox_email_id:integer (required), filename:string, offset:integer |
      | `brain_realign_suggestions` | write | — | Realign Home suggestions with later events, closing or rewriting outdated ones. | suggestion_ids:integer[], limit:integer (default 30, max 60) |
      | `brain_reserve_document_number` | write | deals.edit | Reserve a unique contract/quote/assessment/order number before drafting. | kind:string (required; contract, quote, assessment, order), deal_id:integer, purpose:string |
      | `brain_revise_knowledge_proposal` | write | area owner | Rewrite a pending Knowledge proposal with a note; it stays unpublished. | proposal_id:integer (required), title:string, body_md:string, note:string |
      | `brain_revise_suggestion` | write | — | Correct, reassign, resolve or retype a Home suggestion as instructed. | suggestion_id:integer (required), note:string (required), project_id:integer, action:string, title:string, body:string, resolve:string, change_type:string, assign_to:string |
      | `brain_save_conversation` | write | — | Save the current AI conversation into Nucleo (Inbox → AI) as searchable context. | title:string (required), content:string (required), summary:string, source:string, external_id:string, tags:string[] |
      | `brain_search_companies` | read | companies.view | Search CRM companies by name, email domain or VAT number. | query:string (required), limit:integer (default 10, max 50) |
      | `brain_search_contacts` | read | contacts.view | Search CRM contacts by name, email or phone. | query:string (required), limit:integer (default 10, max 50) |
      | `brain_search_crm` | read | — | Search companies, contacts, deals, projects and tasks at once. | query:string (required), types:string[] (companies, contacts, deals, projects, tasks), limit:integer (1–25, default 5) |
      | `brain_search_deals` | read | deals.view | Search deals by text, stage, owner or company. | query:string, stage:string, owner_id:integer, company_id:integer, limit:integer (default 10, max 50) |
      | `brain_search_emails` | read | inbox.view | Search synced emails with combinable filters. | query:string, company_id:integer, from:string, date_from:string, date_to:string, direction:string (inbound, outbound), pec_only:boolean, with_attachments:boolean, limit:integer (default 15, max 30) |
      | `brain_search_knowledge` | read | — | Search the company Knowledge base you can see. | query:string (required), limit:integer (default 5, max 10) |
      | `brain_search_records` | read | per record type | Search generic records of one type with text, filters, sort and cursor. | type:string (required), query:string, filters:object, sort:string, limit:integer (1–50), cursor:string |
      | `brain_send_message_draft` | write, open world | — | Send one of your drafts (Gmail or Slack, as you) only after explicit confirmation. | draft_id:integer (required), confirmed_by_user:boolean (required) |
      | `brain_send_slack_message` | write, open world | — | Send a Slack message as you to a colleague, a channel or a thread. | to:string, text:string (required), reply_to_message_id:integer |
      | `brain_sign_email_attachment` | write | — | Prepare the e-signature of an emailed PDF and return the editing link. | inbox_email_id:integer (required), filename:string, suggestion_id:integer, list_only:boolean, confirmed_by_user:boolean |
      | `brain_summarize_and_save` | write | — | Save the conversation with title and summary, optionally linked to CRM records. | title:string (required), summary:string (required), content:string (required), source:string, external_id:string, tags:string[], company_id, contact_id, deal_id, project_id:integer |
      | `brain_update_candidate` | write | contacts.edit | Set a job candidate's status with a note. | contact_id:integer (required), status:string (required; reviewed, interview, on_hold, rejected), note:string |
      | `brain_update_company` | write | companies.edit | Update fields of a company. | id:integer (required), name, vat_number, main_domain, website, phone, sector, lifecyclestage, address_city, address_country:string |
      | `brain_update_contact` | write | contacts.edit | Update fields of a contact. | id:integer (required), first_name, last_name, email, phone, mobile, lifecyclestage:string |
      | `brain_update_contract` | write | contracts.edit | Update a contract, or record a withdrawal or cancellation after confirmation. | contract_id:integer (required), status:string, auto_renewal:boolean, effective_end_date:string, cancellation_notice_days:integer, cancellation_terms:string, withdrawal:object |
      | `brain_update_deal` | write | deals.edit | Update a deal's title, value, stage, status or close date. | id:integer (required), title:string, value_cents:integer, currency:string, pipeline_stage_id:integer, pipeline_stage_name:string, status:string (open, won, lost), expected_close_date:string |
      | `brain_update_document_template` | write | templates.manage | Update template metadata; content changes create a new current version. | id:integer (required), code, family, variant:string, name, description, layout:object, blocks:object[], rules, parameters:object, notes:string |
      | `brain_update_message_draft` | write | — | Edit a draft's body, recipients, subject or attachments (does not send). | draft_id:integer (required), body:string, to:string[], cc:string[], subject:string, add_attachments:object[], remove_attachments:string[] |
      | `brain_update_record` | write | per record type | Change fields of a generic record (archiving allowed, never deletes). | type:string (required), id:integer (required), fields:object (required) |
      | `brain_update_task` | write | tasks.manage | Edit a task's title, description, status, priority, due date or assignee. | task_id:integer (required), title:string, description:string, status:string, priority:string, due_date:string, assignee:string, confirmed_by_user:boolean |
      | `brain_cms_<tool>` | per tool: read, write or write, destructive | cms.manage | Tools of the website CMS connected to Brain, listed live from the CMS (only if the company has connected one). Each name is the CMS tool name in snake_case, e.g. `siteMap` → `brain_cms_site_map` (at most 54 characters; longer or clashing names end with a 4-character suffix). Read or write follows the CMS's own annotations, otherwise the verb (`get_`, `list_`, `search_`, `read_`, `find_`… read; anything else writes). Delete tools (`delete_`, `remove_`, `destroy_`, `purge_`, `trash_`, `drop_`, `erase_`) are not exposed. Writes the CMS marks as destructive, or that unpublish, merge, replace, overwrite, reset or revoke, carry `destructiveHint: true`; they and publishing tools need `confirmed_by_user: true`. | defined by the CMS; destructive and publishing tools add confirmed_by_user:boolean (required) |
  - name: "Tools · Catalog"
    description: |
      Products, variants, attributes, translations, categories, price lists, markets and media of the company's catalog. Read tools need membership of the catalog; write tools need the owner, admin or editor role (create_price_list: owner or admin). Small, reversible writes apply at once and are versioned; bigger ones return a preview to confirm in Nucleo.

      27 tools (15 read, 12 write). What Atomo would ask you to confirm in the app (deletions, replacements,
      bulk or storefront changes) returns a preview from an AI client and runs only once confirmed inside
      Nucleo Catalog. The exception is `catalog_forget_memory`, which deletes at once and is marked destructive.

      | Tool | Access | Requires | What it does | Parameters |
      |---|---|---|---|---|
      | `catalog_aggregate_catalog` | read | catalog member | Aggregates (count, avg, sum, min, max) on prices, stock, costs, products or variants, optionally grouped and filtered. | metric:string (required), field:string (required), group_by:string, filters:object |
      | `catalog_bulk_update_products` | write | owner, admin, editor | Bulk-edit a selection or a list of products: status, attributes, categories, channel publication. | selection_id:string, product_ids:integer[], status:string, attributes:object[], add_categories:string[], remove_categories:string[], channel:object |
      | `catalog_catalog_alerts` | read | catalog member | Open catalog alerts (missing coverage, missing attribute values). | stato:string (open, acknowledged, resolved, dismissed; default open) |
      | `catalog_catalog_api` | write | per call | Run Catalog API calls found with catalog_api_reference (up to 50 per call), each checked against your role. | calls:object[] (required; method, path, query, body, grep) |
      | `catalog_catalog_api_reference` | read | catalog member | Search the manual of Catalog functions and endpoints. | search:string, area:string |
      | `catalog_catalog_health` | read | catalog member | Catalog health: coverage of variants, images, translations, types, prices, stock, warehouses. | (none) |
      | `catalog_create_price_list` | write | owner, admin | Create a discounted price list from a base list, for a selection, a list of products or the whole catalog. | label:string (required), discount:number (required), mode:string (percent, amount), rounding:string (none, 00, 90, 95), selection_id:string, product_ids:integer[], all_products:boolean, base_list:string, valid_from:string, valid_to:string, active:boolean |
      | `catalog_create_product` | write | owner, admin, editor | Create a product (draft by default) with generated SKU and handle. | title:string (required), description:string, status:string, type:string, vendor:string, product_sku:string, fill_suggestions:boolean |
      | `catalog_duplicate_product` | write | owner, admin, editor | Duplicate a product as a draft copy (new SKU and handle). | id:integer, query:string |
      | `catalog_export_products` | read | catalog member | Prepare a CSV export of a filtered group of products. | view_id:integer, block:integer, filter:object, title:string |
      | `catalog_fill_product_suggestions` | write | owner, admin, editor | Fill a product with Atomo suggestions (attributes, attribute set, categories) without overwriting different values. | id:integer, query:string |
      | `catalog_forget_memory` | write, destructive | owner, admin, editor | Delete one entry of Atomo's company memory; it cannot be restored. | memory_id:integer (required) |
      | `catalog_get_product` | read | catalog member | Full product: master data, attribute values, variants with prices and stock. | id:integer, sku:string |
      | `catalog_import_history` | read | catalog member | Latest imports with file, status, processed/failed rows and errors. | limit:integer (1–20, default 5) |
      | `catalog_list_categories` | read | catalog member | Category tree with product counts. | (none) |
      | `catalog_list_markets` | read | catalog member | Markets (locale, currency, price list) and catalog languages with translation coverage. | (none) |
      | `catalog_propose_web_values` | write | owner, admin, editor | Queue attribute values found on the web as enrichment proposals to approve, with evidence and source URL. | attribute_code:string (required), locale:string, proposals:object[] (required, max 100) |
      | `catalog_read_attachment` | read | catalog member | Read rows of a CSV/Excel file attached in a Nucleo chat (in-app only). | attachment_id:string (required), offset:integer, limit:integer |
      | `catalog_remember` | write | owner, admin, editor | Save a lasting company convention in Atomo's memory (effective after approval). | text:string (required) |
      | `catalog_save_procedure` | write | owner, admin, editor | Save or update a company procedure. | name:string (required), description:string (required), body:string (required) |
      | `catalog_search_products` | read | catalog member | Search products by text, status, vendor, type, completeness or missing elements (max 20). | query:string, status:string, vendor:string, type:string, min_completeness:number, max_completeness:number, missing_translation:string, without:string[] |
      | `catalog_select_products` | read | catalog member | Build a reusable selection (selection_id) of every product matching the filters, for bulk actions. | query:string, status, vendor, type, category:string[], attributes:object[], price_min, price_max, stock_min, stock_max:number, ids:integer[], without:string[], exclude:object, exclude_ids:integer[], from_selection:string |
      | `catalog_show_view` | read | catalog member | Compose a visual view for the Nucleo app (in-app only). | title:string (required), summary:string, filter:object, blocks:object[] (required, max 8) |
      | `catalog_start_work` | write | owner, admin, editor | Start an Atomo job (findability) that prepares attribute proposals. | kind:string (required; findability), goal:string, locale:string |
      | `catalog_update_product` | write | owner, admin, editor | Edit one product (title, description, status, type, vendor, SKU); versioned and restorable. | id:integer, query:string, title, description, status, type, vendor, product_sku:string |
      | `catalog_use_procedure` | read | catalog member | Read a company procedure to follow it. | name:string (required) |
      | `catalog_workspace_summary` | read | catalog member | Counts of products, categories and promotions. | (none) |
  - name: "Tools · Commerce"
    description: |
      Customer care (tickets and helpdesk requests), POS (points of sale, stock, movements) and Orders (OMS). All read-only. Each group appears only if the company uses it and the person may read it.

      23 tools, all read.

      | Tool | Access | Requires | What it does | Parameters |
      |---|---|---|---|---|
      | `commerce_explain_order` | read | oms.read | Why an order ships from a location or is stuck, with the suggested next action. | order:string (required, max 120) |
      | `commerce_fulfillment_kpis` | read | oms.read | Fulfillment KPIs for a period versus the previous one: speed, splits, holds, rejections, cancellations, returns. | period:string (today, 7d, 30d, 90d, month, last_month; default 30d), from:date, to:date |
      | `commerce_get_customer` | read | oms.read | One customer with orders, returns, spend, average order, first and last order. | customer:string (required; reference, email or order number) |
      | `commerce_get_customer_request` | read | desk.customer_requests.read | One helpdesk request: customer, latest order, linked internal ticket, messages. | id:integer (required) |
      | `commerce_get_order` | read | oms.read | One order in full: customer, address, items, ship-from, holds, shipments with tracking, returns, anomalies. | order:string (required; number or id) |
      | `commerce_get_sellable_stock` | read | oms.read | Sellable stock of a variant per location: reported, reserved, net; optionally net of channel safety stock. | sku:string (required; SKU, EAN or name), channel:string, location:string |
      | `commerce_get_stock` | read | pos.read | On-hand stock per SKU and point of sale; paginated. | location:string, sku:string, page:integer, page_info:string, limit:integer (1–100, default 40) |
      | `commerce_get_stock_movement` | read | pos.read | One goods movement: origin, destination, status, carrier, tracking, transport document, lines, events. | id:string (required, digits) |
      | `commerce_get_ticket` | read | desk.tickets.read | One customer care ticket with the full conversation (does not mark it as read). | ticket:string (required; e.g. 29 or TK-0029) |
      | `commerce_get_ticket_stats` | read | desk.tickets.read | Customer care counters and analytics (SLA, response times, breakdowns, trend). | from:date, to:date |
      | `commerce_list_anomalies` | read | oms.read | Order-flow anomalies with diagnosis and recommended fix. | kind:string, severity:string (info, warning, error), state:string (open, resolved, all; default open), query:string, page:integer, per_page:integer (1–25, default 10) |
      | `commerce_list_at_risk_orders` | read | oms.read | Orders at risk now, worst first, with reason and action. | country:string (ISO-2), reason:string, limit:integer (1–50, default 20) |
      | `commerce_list_customer_requests` | read | desk.customer_requests.read | Up to 50 open helpdesk requests, newest first, with an optional text filter. | query:string |
      | `commerce_list_stock_movements` | read | pos.read | Goods movements (inbound, transfers, returns, in transit), newest first. | bucket:string (inbound, transfer, returns), in_transit:boolean, limit:integer (1–100, default 20) |
      | `commerce_list_store_locations` | read | pos.read | The company's points of sale with type, address and coordinates. | (none) |
      | `commerce_list_ticket_filters` | read | desk.tickets.read | Reference lists for search_tickets: markets, clients, tags, teams. | (none) |
      | `commerce_propose_anomaly_fix` | read | oms.read | Re-diagnose one anomaly with current data and propose fixes. | anomaly:string (required) |
      | `commerce_routing_advice` | read | oms.read | Replay recent orders against the routing rules and propose rule changes with their monthly impact. | days:integer (30, 60 or 90; default 90) |
      | `commerce_search_customers` | read | oms.read | Search customers by name, email or order number. | query:string, country:string, repeat:boolean, sort:string (last, orders, spent, first), page:integer, per_page:integer (1–50) |
      | `commerce_search_orders` | read | oms.read | Search orders by text and filters; returns a page of summaries and the total. | query:string, status:string, channel:string, country:string, location:string, on_hold:boolean, with_anomalies:boolean, from:date, to:date, page:integer, per_page:integer (1–50, default 20) |
      | `commerce_search_returns` | read | oms.read | Search returns, or read one return in full with items and events. | return:string, query:string, status:string, source:string, page:integer, per_page:integer (1–50) |
      | `commerce_search_shipments` | read | oms.read | Shipments newest first, by order or tracking number, status or carrier. | query:string, status:string, carrier:string, page:integer, per_page:integer (1–50) |
      | `commerce_search_tickets` | read | desk.tickets.read | Search customer care tickets by text and filters (open by default); paginated. | query:string, scope:string, status:string, priority:string, tag:string, market_id:integer, client_id:integer, from:date, to:date, sla_breached:boolean, sort:string, direction:string, page:integer, per_page:integer (1–50, default 20) |
  - name: "Tools · Intelligence"
    description: |
      Sales analytics of the company's Shopify store by period, channel, country and product, plus custom dashboards. Read-only; available to every Intelligence role. Periods use the shop's timezone.

      8 tools, all read.

      | Tool | Access | Requires | What it does | Parameters |
      |---|---|---|---|---|
      | `intelligence_get_channel_report` | read | — | Full report of one sales channel for a period. | channel:string (required; ecommerce, retail, b2b, marketplace), marketplace:string, range, from, to, compare, compare_from, compare_to |
      | `intelligence_get_dashboard` | read | — | Read one custom dashboard with its widgets and metrics (private dashboards: owner or admins only). | id:string (required; id or slug) |
      | `intelligence_get_kpi_summary` | read | — | Headline KPIs for a period with comparison and percentage change. | range, from, to, compare, compare_from, compare_to, channel:string, country:string (ISO-2), marketplace:string |
      | `intelligence_get_sales_by_country` | read | — | Sales by country sorted by revenue: orders, units, revenue, net, AOV, return rate, share. | range, from, to, channel:string, limit:integer (1–50, default 10) |
      | `intelligence_get_sales_overview` | read | — | Whole-business revenue by channel with comparison: the starting point. | range, from, to, compare, compare_from, compare_to |
      | `intelligence_get_sales_trend` | read | — | Sales time series by day, week or month, with an optional comparison series. | range, from, to, compare (default none), compare_from, compare_to, measures:string[] (max 7), channel:string, country:string, marketplace:string |
      | `intelligence_get_top_products` | read | — | Best-selling SKUs with units, orders, revenue, returns and rates. | range, from, to, channel:string, sort_by:string (revenue, units, returns), limit:integer (1–50, default 10) |
      | `intelligence_list_dashboards` | read | — | The company's custom dashboards with visibility and whether you can open each. | section:string |
paths:
  /.well-known/oauth-protected-resource:
    get:
      operationId: getProtectedResourceMetadata
      summary: Get protected-resource metadata
      tags: [Discovery]
      security: []
      description: |
        RFC 9728 metadata. `resource` is the root URL of the host you called;
        `authorization_servers` points to Nucleo Core, whose `/.well-known/oauth-authorization-server`
        lists the registration, authorization and token endpoints. Cacheable for 5 minutes.
      responses:
        "200":
          description: Metadata.
          headers:
            Cache-Control:
              schema: { type: string, example: "public, max-age=300" }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ProtectedResourceMetadata" }
              examples:
                production:
                  value:
                    resource: https://mcp.nucleoplatform.com
                    authorization_servers: [https://auth.nucleoplatform.com]
                    bearer_methods_supported: [header]
                    scopes_supported: [openid, profile, email]
                    resource_name: Nucleo
  /.well-known/oauth-protected-resource/{resource}:
    get:
      operationId: getProtectedResourceMetadataForPath
      summary: Get metadata for a resource path
      tags: [Discovery]
      security: []
      description: |
        Path-suffixed variant some clients request (e.g. `/.well-known/oauth-protected-resource/mcp`).
        Same document as the root variant.
      parameters:
        - name: resource
          in: path
          required: true
          schema: { type: string }
          example: mcp
      responses:
        "200":
          description: Metadata.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ProtectedResourceMetadata" }
  /:
    post:
      operationId: mcpRequest
      summary: Send an MCP JSON-RPC request
      tags: [MCP]
      description: |
        The MCP endpoint (Streamable HTTP, `POST` only). Send `Content-Type: application/json` and
        `Authorization: Bearer <access token>`. The response is always a single `application/json`
        JSON-RPC message (`202` with no body for notifications).

        A missing, expired or revoked token gets `401` with
        `WWW-Authenticate: Bearer realm="Nucleo", resource_metadata="https://mcp.nucleoplatform.com/.well-known/oauth-protected-resource"`:
        clients start the OAuth flow from there.

        `tools/call` of a module tool is forwarded to the module with your token, the active company and
        your language; the module's `CallToolResult` (`content`, `structuredContent`, `isError`) is passed
        through unchanged.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/JsonRpcRequest" }
            examples:
              initialize:
                value:
                  jsonrpc: "2.0"
                  id: 1
                  method: initialize
                  params:
                    protocolVersion: "2025-06-18"
                    capabilities: {}
                    clientInfo: { name: acme-agent, version: "1.4.0" }
              initialized:
                summary: Notification (answered with 202)
                value: { jsonrpc: "2.0", method: notifications/initialized }
              toolsList:
                value: { jsonrpc: "2.0", id: 2, method: tools/list }
              listCompanies:
                value:
                  jsonrpc: "2.0"
                  id: 3
                  method: tools/call
                  params: { name: nucleo_list_companies, arguments: {} }
              switchCompany:
                value:
                  jsonrpc: "2.0"
                  id: 4
                  method: tools/call
                  params: { name: nucleo_switch_company, arguments: { company_id: 7 } }
              moduleTool:
                value:
                  jsonrpc: "2.0"
                  id: 5
                  method: tools/call
                  params:
                    name: commerce_search_orders
                    arguments: { query: giulia.rossi@acme.example, status: shipped, per_page: 5 }
              ping:
                value: { jsonrpc: "2.0", id: 6, method: ping }
      responses:
        "200":
          description: JSON-RPC response (result or error).
          content:
            application/json:
              schema: { $ref: "#/components/schemas/JsonRpcResponse" }
              examples:
                initialize:
                  value:
                    jsonrpc: "2.0"
                    id: 1
                    result:
                      protocolVersion: "2025-06-18"
                      capabilities: { tools: { listChanged: false } }
                      serverInfo: { name: nucleo, title: Nucleo, version: "2.0.0" }
                      instructions: "ACTIVE COMPANY: \"Acme Apparel\" (the only company of the user). Modules available here: catalog, commerce. Nucleo is the platform of the signed-in person: tools are grouped by module through their prefix (brain_ = CRM, inbox, knowledge, documents, playbooks; catalog_ = products and assets; commerce_ = tickets, stores, orders; intelligence_ = KPIs and reports). Every tool runs inside the active company with the person's own permissions. Before any write tool, summarise what will change and ask for confirmation; tools marked destructive (destructiveHint) cannot be undone, so say so explicitly. Answer in the user's language (English)."
                toolsList:
                  value:
                    jsonrpc: "2.0"
                    id: 2
                    result:
                      tools:
                        - name: nucleo_list_companies
                          title: List my companies
                          description: List the companies the signed-in person can work in through Nucleo, with the modules available in each and which one is currently active. Call it when the user mentions a company that does not match the active one, or asks what they have access to.
                          inputSchema: { type: object, properties: {} }
                          annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
                        - name: commerce_get_order
                          title: Get order
                          description: "[Commerce] Orders (Nucleo OMS). One order in full: customer, address, items, ship-from location, holds, shipments with tracking, returns and anomalies."
                          inputSchema:
                            type: object
                            properties:
                              order: { type: string, maxLength: 120, description: "Order number (e.g. #1042) or id." }
                            required: [order]
                          annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
                        - name: brain_merge_contacts
                          title: Merge contacts
                          description: "[Brain] Unisce due contatti doppi: primary_contact_id resta, secondary_contact_id confluisce nel principale e finisce nel cestino. … Chiedi conferma; poi chiama con confirmed_by_user=true. …"
                          inputSchema:
                            type: object
                            properties:
                              primary_contact_id: { type: integer, description: Contatto che resta. }
                              secondary_contact_id: { type: integer, description: Contatto doppione da unire nel principale. }
                              confirmed_by_user: { type: boolean, description: "true solo dopo il si' esplicito della persona." }
                            required: [primary_contact_id, secondary_contact_id, confirmed_by_user]
                          annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false }
                listCompanies:
                  value:
                    jsonrpc: "2.0"
                    id: 3
                    result:
                      content:
                        - type: text
                          text: "Companies (● = active):\n● Acme Apparel (id 7) — modules: catalog, commerce\n○ Acme Outlet (id 9) — modules: catalog"
                      structuredContent:
                        companies:
                          - { id: 7, slug: acme, name: Acme Apparel, modules: [catalog, commerce], active: true }
                          - { id: 9, slug: acme-outlet, name: Acme Outlet, modules: [catalog], active: false }
                        active_company_id: 7
                      isError: false
                switchCompany:
                  value:
                    jsonrpc: "2.0"
                    id: 4
                    result:
                      content: [ { type: text, text: "Active company is now \"Acme Outlet\" (id 9). Module tools now work inside it." } ]
                      structuredContent: { active_company_id: 9 }
                      isError: false
                toolError:
                  summary: Tool-level error (isError)
                  value:
                    jsonrpc: "2.0"
                    id: 5
                    result:
                      content: [ { type: text, text: "You do not have permission `oms.read` for this action." } ]
                      isError: true
                unknownTool:
                  value: { jsonrpc: "2.0", id: 5, error: { code: -32602, message: "Unknown tool: commerce_search_invoices" } }
                methodNotFound:
                  value: { jsonrpc: "2.0", id: 8, error: { code: -32601, message: "Method not found: resources/list" } }
                busy:
                  value: { jsonrpc: "2.0", id: 5, error: { code: -32000, message: "Nucleo Commerce is busy, retry in 30 seconds." } }
        "202":
          description: Notification accepted (no body).
        "400":
          description: Body is not a JSON-RPC 2.0 request (also returned for batches).
          content:
            application/json:
              schema: { $ref: "#/components/schemas/JsonRpcResponse" }
              examples:
                parse:
                  value: { jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error: expected a JSON-RPC 2.0 request." } }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "413":
          description: Request body larger than 2 MB.
    get:
      operationId: mcpGetNotAllowed
      summary: Server stream (not supported)
      tags: [MCP]
      security: []
      description: "Server-initiated SSE streams are not offered. Always `405` with `Allow: POST`."
      responses:
        "405": { $ref: "#/components/responses/MethodNotAllowed" }
  /mcp:
    post:
      operationId: mcpRequestAlias
      summary: Send an MCP request (alias)
      tags: [MCP]
      description: Same as `POST /`, for clients that expect an `/mcp` path.
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/JsonRpcRequest" }
      responses:
        "200":
          description: JSON-RPC response.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/JsonRpcResponse" }
        "202":
          description: Notification accepted.
        "400":
          description: Not a JSON-RPC 2.0 request.
        "401": { $ref: "#/components/responses/Unauthorized" }
    get:
      operationId: mcpAliasGetNotAllowed
      summary: Server stream on alias (not supported)
      tags: [MCP]
      security: []
      description: "Always `405` with `Allow: POST`."
      responses:
        "405": { $ref: "#/components/responses/MethodNotAllowed" }
components:
  securitySchemes:
    nucleoOAuth:
      type: oauth2
      description: |
        Access token from Nucleo Core. MCP clients obtain it automatically: protected-resource metadata →
        authorization server metadata → dynamic client registration → authorization code + PKCE (S256)
        → token (8 h) + refresh token (30 days). The user approves the client on a consent screen.
        Tokens of self-registered clients carry `nucleo.ai_tools` and work only with the AI tools.
      flows:
        authorizationCode:
          authorizationUrl: https://auth.nucleoplatform.com/oauth/authorize
          tokenUrl: https://auth.nucleoplatform.com/oauth/token
          refreshUrl: https://auth.nucleoplatform.com/oauth/token
          scopes:
            openid: OpenID Connect sign-in
            profile: Name and avatar
            email: Email address
            offline_access: Refresh token
  responses:
    Unauthorized:
      description: Missing, invalid, expired or revoked token (or Nucleo Core temporarily unreachable).
      headers:
        WWW-Authenticate:
          schema:
            type: string
            example: 'Bearer realm="Nucleo", resource_metadata="https://mcp.nucleoplatform.com/.well-known/oauth-protected-resource"'
      content:
        application/json:
          schema:
            type: object
            properties:
              error: { type: string }
              message: { type: string }
          examples:
            unauthorized:
              value: { error: unauthorized, message: Sign in with Nucleo to use this connector. }
    MethodNotAllowed:
      description: Only POST is supported.
      headers:
        Allow:
          schema: { type: string, example: POST }
      content:
        application/json:
          schema:
            type: object
            properties:
              error: { type: string }
              message: { type: string }
          examples:
            notAllowed:
              value: { error: method_not_allowed, message: Use POST with a JSON-RPC 2.0 body. }
  schemas:
    ProtectedResourceMetadata:
      type: object
      required: [resource, authorization_servers]
      properties:
        resource: { type: string, format: uri }
        authorization_servers: { type: array, items: { type: string, format: uri } }
        bearer_methods_supported: { type: array, items: { type: string, enum: [header] } }
        scopes_supported: { type: array, items: { type: string } }
        resource_name: { type: string }
    JsonRpcRequest:
      type: object
      required: [jsonrpc, method]
      properties:
        jsonrpc: { type: string, const: "2.0" }
        id:
          type: [string, integer]
          description: Omit for notifications.
        method:
          type: string
          enum: [initialize, ping, tools/list, tools/call, notifications/initialized, notifications/cancelled]
        params:
          type: object
          description: |
            `initialize`: `protocolVersion`, `capabilities`, `clientInfo {name, version}`.
            `tools/call`: `name` (prefixed tool name) and `arguments` (object matching the tool's `inputSchema`).
          additionalProperties: true
    JsonRpcResponse:
      type: object
      required: [jsonrpc, id]
      properties:
        jsonrpc: { type: string, const: "2.0" }
        id: { type: [string, integer, "null"] }
        result:
          description: "`initialize` → InitializeResult; `tools/list` → `{tools: Tool[]}`; `tools/call` → CallToolResult; `ping` → `{}`."
          type: object
          additionalProperties: true
        error:
          type: object
          properties:
            code: { type: integer, enum: [-32700, -32601, -32602, -32603, -32000] }
            message: { type: string }
    Tool:
      type: object
      required: [name, inputSchema]
      properties:
        name: { type: string, maxLength: 64, description: "Prefixed: `brain_`, `catalog_`, `commerce_`, `intelligence_` or `nucleo_`." }
        title: { type: string, maxLength: 60 }
        description: { type: string, description: "Module tools start with `[Brain]`, `[Catalog]`, `[Commerce]` or `[Intelligence]`." }
        inputSchema: { type: object }
        annotations:
          type: object
          properties:
            readOnlyHint: { type: boolean, description: "true when the tool only reads. A write tool is never read-only." }
            destructiveHint: { type: boolean, description: "true for a write that cannot be reversed automatically (Brain merges, catalog_forget_memory, destructive website CMS tools). Always false for read tools." }
            idempotentHint: { type: boolean, description: "true when calling it again with the same arguments changes nothing more (reads)." }
            openWorldHint: { type: boolean, description: "true when the tool reaches people outside Nucleo (sending an email or a Slack message)." }
    CallToolResult:
      type: object
      required: [content, isError]
      properties:
        content:
          type: array
          items:
            type: object
            properties:
              type: { type: string, enum: [text, resource_link, image] }
              text: { type: string }
        structuredContent: { type: object, additionalProperties: true }
        isError: { type: boolean }
x-nucleo-tools:
  - module: "Nucleo (gateway)"
    prefix: "nucleo_"
    description: "Always available. Company selection for every module tool."
    tools:
      - name: "nucleo_list_companies"
        access: "read"
        requires: null
        summary: "List the companies you can work in, the modules available in each through the connector, and which one is active."
        params: "(none)"
      - name: "nucleo_switch_company"
        access: "write"
        requires: null
        summary: "Make another company the active one, for this and the next sessions; every module tool then works inside it."
        params: "company_id:integer (required, from nucleo_list_companies)"
  - module: "Brain"
    prefix: "brain_"
    description: "CRM, inbox and timeline, Knowledge and playbooks, sales documents, tasks, saved AI conversations, GDPR requests (read-only) and generic records. Visible tools depend on the person's Brain permissions."
    tools:
      - name: "brain_add_contact_email"
        access: "write"
        requires: "contacts.edit"
        summary: "Add an alias email to an existing contact without changing the primary email."
        params: "contact_id:integer (required), email:string (required)"
      - name: "brain_add_note"
        access: "write"
        requires: "contacts.edit"
        summary: "Add a note to a contact, company or deal."
        params: "entity:string (required; contact, company, deal), entity_id:integer (required), subject:string, body:string (required)"
      - name: "brain_attach_email_attachment_to_deal"
        access: "write"
        requires: "deals.edit"
        summary: "Attach an Inbox email attachment to a deal as its contract or as a deal document."
        params: "inbox_email_id:integer (required), deal_id:integer (required), as:string (contract, document; default contract), filenames:string[], signed:boolean, mark_won:boolean, doc_type:string"
      - name: "brain_create_company"
        access: "write"
        requires: "companies.edit"
        summary: "Create a CRM company."
        params: "name:string (required), vat_number, main_domain, website, phone, sector, lifecyclestage, address_city, address_country:string"
      - name: "brain_create_contact"
        access: "write"
        requires: "contacts.edit"
        summary: "Create a CRM contact."
        params: "first_name:string (required), last_name:string (required), email, phone, mobile:string, company_id:integer, lifecyclestage:string"
      - name: "brain_create_deal"
        access: "write"
        requires: "deals.create"
        summary: "Create a deal, optionally from an email or a suggestion; warns about existing open deals."
        params: "title:string (required), company_id:integer, company_name:string, create_company:boolean, contact_id:integer, stage:string, value_eur:number, expected_close_date:string, note:string, inbox_email_id:integer, suggestion_id:integer, confirm_new:boolean"
      - name: "brain_create_deal_document"
        access: "write"
        requires: "deals.edit"
        summary: "Save an AI-written deal document (brief, assessment, quote, contract…) as a numbered HTML draft."
        params: "deal_id:integer (required), title:string (required), doc_type:string (required), html:string (required), format:string (a4, deck), locale:string (it, en), valid_until:string, document_template_id:integer, document_id:integer, contract:object, document_number:string"
      - name: "brain_create_document_template"
        access: "write"
        requires: "templates.manage"
        summary: "Create a document template as a draft, mirroring an existing template's structure."
        params: "kind:string (required; assessment, quote, contract, order, tc), code:string (max 12), family:string, variant:string, name:object (required, en/it), description:object, layout:object, blocks:object[] (required), rules:object, parameters:object, notes:string"
      - name: "brain_create_message_draft"
        access: "write"
        requires: null
        summary: "Create an email or Slack message draft (nothing is sent), new or reply-all, with attachments."
        params: "channel:string (email, slack), inbox_email_id:integer, to:string[], cc:string[], subject:string, body:string (required), reply_to_message_id:integer, slack_to:string, attachments:object[]"
      - name: "brain_create_record"
        access: "write"
        requires: "per record type"
        summary: "Create a generic Brain record of a given type (see list_record_types)."
        params: "type:string (required), fields:object (required)"
      - name: "brain_create_task"
        access: "write"
        requires: null
        summary: "Create a task (reuses an identical open one), optionally assigned and in a project."
        params: "title:string (required), description:string, due_date:string, assignee:string, project_id:integer, suggestion_id:integer"
      - name: "brain_get_chat_message"
        access: "read"
        requires: null
        summary: "Read a received Slack message with its recent conversation."
        params: "message_id:integer (required)"
      - name: "brain_get_company"
        access: "read"
        requires: "companies.view"
        summary: "Company details with linked contacts and active deals."
        params: "id:integer (required)"
      - name: "brain_get_company_brief"
        access: "read"
        requires: null
        summary: "One-call company summary: relationship, contacts, deals, contracts, calls, saved AI conversations."
        params: "company_id:integer, name:string"
      - name: "brain_get_company_timeline"
        access: "read"
        requires: "companies.view"
        summary: "Recent company context: deals, timeline activities and saved AI conversations."
        params: "company_id:integer (required), days:integer (7–730, default 180), limit:integer (5–50, default 20)"
      - name: "brain_get_contact"
        access: "read"
        requires: "contacts.view"
        summary: "Contact details with company and recent deals."
        params: "id:integer (required)"
      - name: "brain_get_contract"
        access: "read"
        requires: "contracts.view"
        summary: "Contract status, dates, renewal, notice, amount, withdrawal and linked quotes."
        params: "contract_id:integer (required)"
      - name: "brain_get_deal"
        access: "read"
        requires: "deals.view"
        summary: "Deal details: company, contacts, owner, stage, services."
        params: "id:integer (required)"
      - name: "brain_get_document_template"
        access: "read"
        requires: "templates.view"
        summary: "Read a document template (layout, blocks, rules, parameters) by id or code."
        params: "id:integer, code:string"
      - name: "brain_get_email"
        access: "read"
        requires: null
        summary: "Read an Inbox email with body, attachments, thread and linked company/deal."
        params: "inbox_email_id:integer (required)"
      - name: "brain_get_knowledge_proposal"
        access: "read"
        requires: "area owner"
        summary: "Read a pending Knowledge change proposal with the current page text."
        params: "proposal_id:integer (required)"
      - name: "brain_get_playbook"
        access: "read"
        requires: null
        summary: "Read the Knowledge playbook or procedure on a topic, or a specific page."
        params: "topic:string, page_id:integer, slug:string, limit:integer (1–5, default 2)"
      - name: "brain_get_privacy_request"
        access: "read"
        requires: "gdpr.manage"
        summary: "Read a GDPR request by id or reference, with its event log."
        params: "id:integer, reference:string, events_limit:integer (1–100, default 30)"
      - name: "brain_get_quote"
        access: "read"
        requires: "quotes.view"
        summary: "Read a quote with status, totals and line items."
        params: "quote_id:integer (required)"
      - name: "brain_get_record"
        access: "read"
        requires: "per record type"
        summary: "Read one generic record with fields, links, related records and URL."
        params: "type:string (required), id:integer (required)"
      - name: "brain_list_calendar_events"
        access: "read"
        requires: null
        summary: "List your own Google Calendar events in a date range (max 62 days)."
        params: "from:string (date, default today), to:string (date), query:string"
      - name: "brain_list_deal_documents"
        access: "read"
        requires: "deals.view"
        summary: "List a deal's documents with number, status, language, validity and versions."
        params: "deal_id:integer (required), doc_type:string"
      - name: "brain_list_document_templates"
        access: "read"
        requires: "templates.view"
        summary: "List active document templates with code, variant and parameters."
        params: "kind:string (assessment, quote, contract, order, tc), include_archived:boolean"
      - name: "brain_list_message_drafts"
        access: "read"
        requires: null
        summary: "List your open email and Slack drafts."
        params: "limit:integer (default 15, max 30)"
      - name: "brain_list_my_tasks"
        access: "read"
        requires: null
        summary: "List your open tasks, soonest due first."
        params: "limit:integer (1–50, default 20), overdue_only:boolean"
      - name: "brain_list_privacy_requests"
        access: "read"
        requires: "gdpr.manage"
        summary: "List GDPR requests with status, deadline and assignee."
        params: "status:string (default open), type:string, search:string, overdue_only:boolean, limit:integer (1–50, default 20)"
      - name: "brain_list_record_types"
        access: "read"
        requires: null
        summary: "Manual of the generic record types and fields you can use."
        params: "type:string"
      - name: "brain_list_tasks"
        access: "read"
        requires: null
        summary: "List open tasks (yours, a colleague's or a project's) by urgency."
        params: "assignee:string, project_id:integer, overdue_only:boolean, due_within_days:integer, limit:integer (default 15, max 50)"
      - name: "brain_log_note"
        access: "write"
        requires: null
        summary: "Add a note to a contact, company or deal timeline."
        params: "body:string (required), subject:string, contact_id:integer, company_id:integer, deal_id:integer"
      - name: "brain_merge_contacts"
        access: "write, destructive"
        requires: "contacts.edit"
        summary: "Merge a duplicate contact into the primary one after explicit confirmation (the duplicate goes to the trash)."
        params: "primary_contact_id:integer (required), secondary_contact_id:integer (required), confirmed_by_user:boolean (required)"
      - name: "brain_merge_tasks"
        access: "write, destructive"
        requires: "tasks.manage"
        summary: "Find similar duplicate tasks, or merge them into one after confirmation."
        params: "task_id:integer, list_only:boolean, keep_id:integer, merge_ids:integer[], confirmed_by_user:boolean"
      - name: "brain_propose_knowledge_page"
        access: "write"
        requires: null
        summary: "Propose a new or updated Knowledge page for the area owner to approve."
        params: "area_slug:string (required), title:string (required), body_md:string (required), kind:string, summary:string, rationale:string (required), page_id:integer"
      - name: "brain_read_drive_file"
        access: "read"
        requires: null
        summary: "Open a Google Drive/Docs file by link or id and return its text."
        params: "url:string (required), offset:integer"
      - name: "brain_read_email_attachment"
        access: "read"
        requires: "inbox.view"
        summary: "Read the text of an Inbox email attachment (PDF, Word, images)."
        params: "inbox_email_id:integer (required), filename:string, offset:integer"
      - name: "brain_realign_suggestions"
        access: "write"
        requires: null
        summary: "Realign Home suggestions with later events, closing or rewriting outdated ones."
        params: "suggestion_ids:integer[], limit:integer (default 30, max 60)"
      - name: "brain_reserve_document_number"
        access: "write"
        requires: "deals.edit"
        summary: "Reserve a unique contract/quote/assessment/order number before drafting."
        params: "kind:string (required; contract, quote, assessment, order), deal_id:integer, purpose:string"
      - name: "brain_revise_knowledge_proposal"
        access: "write"
        requires: "area owner"
        summary: "Rewrite a pending Knowledge proposal with a note; it stays unpublished."
        params: "proposal_id:integer (required), title:string, body_md:string, note:string"
      - name: "brain_revise_suggestion"
        access: "write"
        requires: null
        summary: "Correct, reassign, resolve or retype a Home suggestion as instructed."
        params: "suggestion_id:integer (required), note:string (required), project_id:integer, action:string, title:string, body:string, resolve:string, change_type:string, assign_to:string"
      - name: "brain_save_conversation"
        access: "write"
        requires: null
        summary: "Save the current AI conversation into Nucleo (Inbox → AI) as searchable context."
        params: "title:string (required), content:string (required), summary:string, source:string, external_id:string, tags:string[]"
      - name: "brain_search_companies"
        access: "read"
        requires: "companies.view"
        summary: "Search CRM companies by name, email domain or VAT number."
        params: "query:string (required), limit:integer (default 10, max 50)"
      - name: "brain_search_contacts"
        access: "read"
        requires: "contacts.view"
        summary: "Search CRM contacts by name, email or phone."
        params: "query:string (required), limit:integer (default 10, max 50)"
      - name: "brain_search_crm"
        access: "read"
        requires: null
        summary: "Search companies, contacts, deals, projects and tasks at once."
        params: "query:string (required), types:string[] (companies, contacts, deals, projects, tasks), limit:integer (1–25, default 5)"
      - name: "brain_search_deals"
        access: "read"
        requires: "deals.view"
        summary: "Search deals by text, stage, owner or company."
        params: "query:string, stage:string, owner_id:integer, company_id:integer, limit:integer (default 10, max 50)"
      - name: "brain_search_emails"
        access: "read"
        requires: "inbox.view"
        summary: "Search synced emails with combinable filters."
        params: "query:string, company_id:integer, from:string, date_from:string, date_to:string, direction:string (inbound, outbound), pec_only:boolean, with_attachments:boolean, limit:integer (default 15, max 30)"
      - name: "brain_search_knowledge"
        access: "read"
        requires: null
        summary: "Search the company Knowledge base you can see."
        params: "query:string (required), limit:integer (default 5, max 10)"
      - name: "brain_search_records"
        access: "read"
        requires: "per record type"
        summary: "Search generic records of one type with text, filters, sort and cursor."
        params: "type:string (required), query:string, filters:object, sort:string, limit:integer (1–50), cursor:string"
      - name: "brain_send_message_draft"
        access: "write, open world"
        requires: null
        summary: "Send one of your drafts (Gmail or Slack, as you) only after explicit confirmation."
        params: "draft_id:integer (required), confirmed_by_user:boolean (required)"
      - name: "brain_send_slack_message"
        access: "write, open world"
        requires: null
        summary: "Send a Slack message as you to a colleague, a channel or a thread."
        params: "to:string, text:string (required), reply_to_message_id:integer"
      - name: "brain_sign_email_attachment"
        access: "write"
        requires: null
        summary: "Prepare the e-signature of an emailed PDF and return the editing link."
        params: "inbox_email_id:integer (required), filename:string, suggestion_id:integer, list_only:boolean, confirmed_by_user:boolean"
      - name: "brain_summarize_and_save"
        access: "write"
        requires: null
        summary: "Save the conversation with title and summary, optionally linked to CRM records."
        params: "title:string (required), summary:string (required), content:string (required), source:string, external_id:string, tags:string[], company_id, contact_id, deal_id, project_id:integer"
      - name: "brain_update_candidate"
        access: "write"
        requires: "contacts.edit"
        summary: "Set a job candidate's status with a note."
        params: "contact_id:integer (required), status:string (required; reviewed, interview, on_hold, rejected), note:string"
      - name: "brain_update_company"
        access: "write"
        requires: "companies.edit"
        summary: "Update fields of a company."
        params: "id:integer (required), name, vat_number, main_domain, website, phone, sector, lifecyclestage, address_city, address_country:string"
      - name: "brain_update_contact"
        access: "write"
        requires: "contacts.edit"
        summary: "Update fields of a contact."
        params: "id:integer (required), first_name, last_name, email, phone, mobile, lifecyclestage:string"
      - name: "brain_update_contract"
        access: "write"
        requires: "contracts.edit"
        summary: "Update a contract, or record a withdrawal or cancellation after confirmation."
        params: "contract_id:integer (required), status:string, auto_renewal:boolean, effective_end_date:string, cancellation_notice_days:integer, cancellation_terms:string, withdrawal:object"
      - name: "brain_update_deal"
        access: "write"
        requires: "deals.edit"
        summary: "Update a deal's title, value, stage, status or close date."
        params: "id:integer (required), title:string, value_cents:integer, currency:string, pipeline_stage_id:integer, pipeline_stage_name:string, status:string (open, won, lost), expected_close_date:string"
      - name: "brain_update_document_template"
        access: "write"
        requires: "templates.manage"
        summary: "Update template metadata; content changes create a new current version."
        params: "id:integer (required), code, family, variant:string, name, description, layout:object, blocks:object[], rules, parameters:object, notes:string"
      - name: "brain_update_message_draft"
        access: "write"
        requires: null
        summary: "Edit a draft's body, recipients, subject or attachments (does not send)."
        params: "draft_id:integer (required), body:string, to:string[], cc:string[], subject:string, add_attachments:object[], remove_attachments:string[]"
      - name: "brain_update_record"
        access: "write"
        requires: "per record type"
        summary: "Change fields of a generic record (archiving allowed, never deletes)."
        params: "type:string (required), id:integer (required), fields:object (required)"
      - name: "brain_update_task"
        access: "write"
        requires: "tasks.manage"
        summary: "Edit a task's title, description, status, priority, due date or assignee."
        params: "task_id:integer (required), title:string, description:string, status:string, priority:string, due_date:string, assignee:string, confirmed_by_user:boolean"
      - name: "brain_cms_<tool>"
        access: "per tool: read, write or write, destructive"
        requires: "cms.manage"
        summary: "Tools of the website CMS connected to Brain, listed live from the CMS (only if the company has connected one). Each name is the CMS tool name in snake_case, e.g. `siteMap` → `brain_cms_site_map` (at most 54 characters; longer or clashing names end with a 4-character suffix). Read or write follows the CMS's own annotations, otherwise the verb (`get_`, `list_`, `search_`, `read_`, `find_`… read; anything else writes). Delete tools (`delete_`, `remove_`, `destroy_`, `purge_`, `trash_`, `drop_`, `erase_`) are not exposed. Writes the CMS marks as destructive, or that unpublish, merge, replace, overwrite, reset or revoke, carry `destructiveHint: true`; they and publishing tools need `confirmed_by_user: true`."
        params: "defined by the CMS; destructive and publishing tools add confirmed_by_user:boolean (required)"
  - module: "Catalog"
    prefix: "catalog_"
    description: "Products, variants, attributes, translations, categories, price lists, markets and media of the company's catalog. Read tools need membership of the catalog; write tools need the owner, admin or editor role (create_price_list: owner or admin). Small, reversible writes apply at once and are versioned; bigger ones return a preview to confirm in Nucleo."
    tools:
      - name: "catalog_aggregate_catalog"
        access: "read"
        requires: "catalog member"
        summary: "Aggregates (count, avg, sum, min, max) on prices, stock, costs, products or variants, optionally grouped and filtered."
        params: "metric:string (required), field:string (required), group_by:string, filters:object"
      - name: "catalog_bulk_update_products"
        access: "write"
        requires: "owner, admin, editor"
        summary: "Bulk-edit a selection or a list of products: status, attributes, categories, channel publication."
        params: "selection_id:string, product_ids:integer[], status:string, attributes:object[], add_categories:string[], remove_categories:string[], channel:object"
      - name: "catalog_catalog_alerts"
        access: "read"
        requires: "catalog member"
        summary: "Open catalog alerts (missing coverage, missing attribute values)."
        params: "stato:string (open, acknowledged, resolved, dismissed; default open)"
      - name: "catalog_catalog_api"
        access: "write"
        requires: "per call"
        summary: "Run Catalog API calls found with catalog_api_reference (up to 50 per call), each checked against your role."
        params: "calls:object[] (required; method, path, query, body, grep)"
      - name: "catalog_catalog_api_reference"
        access: "read"
        requires: "catalog member"
        summary: "Search the manual of Catalog functions and endpoints."
        params: "search:string, area:string"
      - name: "catalog_catalog_health"
        access: "read"
        requires: "catalog member"
        summary: "Catalog health: coverage of variants, images, translations, types, prices, stock, warehouses."
        params: "(none)"
      - name: "catalog_create_price_list"
        access: "write"
        requires: "owner, admin"
        summary: "Create a discounted price list from a base list, for a selection, a list of products or the whole catalog."
        params: "label:string (required), discount:number (required), mode:string (percent, amount), rounding:string (none, 00, 90, 95), selection_id:string, product_ids:integer[], all_products:boolean, base_list:string, valid_from:string, valid_to:string, active:boolean"
      - name: "catalog_create_product"
        access: "write"
        requires: "owner, admin, editor"
        summary: "Create a product (draft by default) with generated SKU and handle."
        params: "title:string (required), description:string, status:string, type:string, vendor:string, product_sku:string, fill_suggestions:boolean"
      - name: "catalog_duplicate_product"
        access: "write"
        requires: "owner, admin, editor"
        summary: "Duplicate a product as a draft copy (new SKU and handle)."
        params: "id:integer, query:string"
      - name: "catalog_export_products"
        access: "read"
        requires: "catalog member"
        summary: "Prepare a CSV export of a filtered group of products."
        params: "view_id:integer, block:integer, filter:object, title:string"
      - name: "catalog_fill_product_suggestions"
        access: "write"
        requires: "owner, admin, editor"
        summary: "Fill a product with Atomo suggestions (attributes, attribute set, categories) without overwriting different values."
        params: "id:integer, query:string"
      - name: "catalog_forget_memory"
        access: "write, destructive"
        requires: "owner, admin, editor"
        summary: "Delete one entry of Atomo's company memory; it cannot be restored."
        params: "memory_id:integer (required)"
      - name: "catalog_get_product"
        access: "read"
        requires: "catalog member"
        summary: "Full product: master data, attribute values, variants with prices and stock."
        params: "id:integer, sku:string"
      - name: "catalog_import_history"
        access: "read"
        requires: "catalog member"
        summary: "Latest imports with file, status, processed/failed rows and errors."
        params: "limit:integer (1–20, default 5)"
      - name: "catalog_list_categories"
        access: "read"
        requires: "catalog member"
        summary: "Category tree with product counts."
        params: "(none)"
      - name: "catalog_list_markets"
        access: "read"
        requires: "catalog member"
        summary: "Markets (locale, currency, price list) and catalog languages with translation coverage."
        params: "(none)"
      - name: "catalog_propose_web_values"
        access: "write"
        requires: "owner, admin, editor"
        summary: "Queue attribute values found on the web as enrichment proposals to approve, with evidence and source URL."
        params: "attribute_code:string (required), locale:string, proposals:object[] (required, max 100)"
      - name: "catalog_read_attachment"
        access: "read"
        requires: "catalog member"
        summary: "Read rows of a CSV/Excel file attached in a Nucleo chat (in-app only)."
        params: "attachment_id:string (required), offset:integer, limit:integer"
      - name: "catalog_remember"
        access: "write"
        requires: "owner, admin, editor"
        summary: "Save a lasting company convention in Atomo's memory (effective after approval)."
        params: "text:string (required)"
      - name: "catalog_save_procedure"
        access: "write"
        requires: "owner, admin, editor"
        summary: "Save or update a company procedure."
        params: "name:string (required), description:string (required), body:string (required)"
      - name: "catalog_search_products"
        access: "read"
        requires: "catalog member"
        summary: "Search products by text, status, vendor, type, completeness or missing elements (max 20)."
        params: "query:string, status:string, vendor:string, type:string, min_completeness:number, max_completeness:number, missing_translation:string, without:string[]"
      - name: "catalog_select_products"
        access: "read"
        requires: "catalog member"
        summary: "Build a reusable selection (selection_id) of every product matching the filters, for bulk actions."
        params: "query:string, status, vendor, type, category:string[], attributes:object[], price_min, price_max, stock_min, stock_max:number, ids:integer[], without:string[], exclude:object, exclude_ids:integer[], from_selection:string"
      - name: "catalog_show_view"
        access: "read"
        requires: "catalog member"
        summary: "Compose a visual view for the Nucleo app (in-app only)."
        params: "title:string (required), summary:string, filter:object, blocks:object[] (required, max 8)"
      - name: "catalog_start_work"
        access: "write"
        requires: "owner, admin, editor"
        summary: "Start an Atomo job (findability) that prepares attribute proposals."
        params: "kind:string (required; findability), goal:string, locale:string"
      - name: "catalog_update_product"
        access: "write"
        requires: "owner, admin, editor"
        summary: "Edit one product (title, description, status, type, vendor, SKU); versioned and restorable."
        params: "id:integer, query:string, title, description, status, type, vendor, product_sku:string"
      - name: "catalog_use_procedure"
        access: "read"
        requires: "catalog member"
        summary: "Read a company procedure to follow it."
        params: "name:string (required)"
      - name: "catalog_workspace_summary"
        access: "read"
        requires: "catalog member"
        summary: "Counts of products, categories and promotions."
        params: "(none)"
  - module: "Commerce"
    prefix: "commerce_"
    description: "Customer care (tickets and helpdesk requests), POS (points of sale, stock, movements) and Orders (OMS). All read-only. Each group appears only if the company uses it and the person may read it."
    tools:
      - name: "commerce_explain_order"
        access: "read"
        requires: "oms.read"
        summary: "Why an order ships from a location or is stuck, with the suggested next action."
        params: "order:string (required, max 120)"
      - name: "commerce_fulfillment_kpis"
        access: "read"
        requires: "oms.read"
        summary: "Fulfillment KPIs for a period versus the previous one: speed, splits, holds, rejections, cancellations, returns."
        params: "period:string (today, 7d, 30d, 90d, month, last_month; default 30d), from:date, to:date"
      - name: "commerce_get_customer"
        access: "read"
        requires: "oms.read"
        summary: "One customer with orders, returns, spend, average order, first and last order."
        params: "customer:string (required; reference, email or order number)"
      - name: "commerce_get_customer_request"
        access: "read"
        requires: "desk.customer_requests.read"
        summary: "One helpdesk request: customer, latest order, linked internal ticket, messages."
        params: "id:integer (required)"
      - name: "commerce_get_order"
        access: "read"
        requires: "oms.read"
        summary: "One order in full: customer, address, items, ship-from, holds, shipments with tracking, returns, anomalies."
        params: "order:string (required; number or id)"
      - name: "commerce_get_sellable_stock"
        access: "read"
        requires: "oms.read"
        summary: "Sellable stock of a variant per location: reported, reserved, net; optionally net of channel safety stock."
        params: "sku:string (required; SKU, EAN or name), channel:string, location:string"
      - name: "commerce_get_stock"
        access: "read"
        requires: "pos.read"
        summary: "On-hand stock per SKU and point of sale; paginated."
        params: "location:string, sku:string, page:integer, page_info:string, limit:integer (1–100, default 40)"
      - name: "commerce_get_stock_movement"
        access: "read"
        requires: "pos.read"
        summary: "One goods movement: origin, destination, status, carrier, tracking, transport document, lines, events."
        params: "id:string (required, digits)"
      - name: "commerce_get_ticket"
        access: "read"
        requires: "desk.tickets.read"
        summary: "One customer care ticket with the full conversation (does not mark it as read)."
        params: "ticket:string (required; e.g. 29 or TK-0029)"
      - name: "commerce_get_ticket_stats"
        access: "read"
        requires: "desk.tickets.read"
        summary: "Customer care counters and analytics (SLA, response times, breakdowns, trend)."
        params: "from:date, to:date"
      - name: "commerce_list_anomalies"
        access: "read"
        requires: "oms.read"
        summary: "Order-flow anomalies with diagnosis and recommended fix."
        params: "kind:string, severity:string (info, warning, error), state:string (open, resolved, all; default open), query:string, page:integer, per_page:integer (1–25, default 10)"
      - name: "commerce_list_at_risk_orders"
        access: "read"
        requires: "oms.read"
        summary: "Orders at risk now, worst first, with reason and action."
        params: "country:string (ISO-2), reason:string, limit:integer (1–50, default 20)"
      - name: "commerce_list_customer_requests"
        access: "read"
        requires: "desk.customer_requests.read"
        summary: "Up to 50 open helpdesk requests, newest first, with an optional text filter."
        params: "query:string"
      - name: "commerce_list_stock_movements"
        access: "read"
        requires: "pos.read"
        summary: "Goods movements (inbound, transfers, returns, in transit), newest first."
        params: "bucket:string (inbound, transfer, returns), in_transit:boolean, limit:integer (1–100, default 20)"
      - name: "commerce_list_store_locations"
        access: "read"
        requires: "pos.read"
        summary: "The company's points of sale with type, address and coordinates."
        params: "(none)"
      - name: "commerce_list_ticket_filters"
        access: "read"
        requires: "desk.tickets.read"
        summary: "Reference lists for search_tickets: markets, clients, tags, teams."
        params: "(none)"
      - name: "commerce_propose_anomaly_fix"
        access: "read"
        requires: "oms.read"
        summary: "Re-diagnose one anomaly with current data and propose fixes."
        params: "anomaly:string (required)"
      - name: "commerce_routing_advice"
        access: "read"
        requires: "oms.read"
        summary: "Replay recent orders against the routing rules and propose rule changes with their monthly impact."
        params: "days:integer (30, 60 or 90; default 90)"
      - name: "commerce_search_customers"
        access: "read"
        requires: "oms.read"
        summary: "Search customers by name, email or order number."
        params: "query:string, country:string, repeat:boolean, sort:string (last, orders, spent, first), page:integer, per_page:integer (1–50)"
      - name: "commerce_search_orders"
        access: "read"
        requires: "oms.read"
        summary: "Search orders by text and filters; returns a page of summaries and the total."
        params: "query:string, status:string, channel:string, country:string, location:string, on_hold:boolean, with_anomalies:boolean, from:date, to:date, page:integer, per_page:integer (1–50, default 20)"
      - name: "commerce_search_returns"
        access: "read"
        requires: "oms.read"
        summary: "Search returns, or read one return in full with items and events."
        params: "return:string, query:string, status:string, source:string, page:integer, per_page:integer (1–50)"
      - name: "commerce_search_shipments"
        access: "read"
        requires: "oms.read"
        summary: "Shipments newest first, by order or tracking number, status or carrier."
        params: "query:string, status:string, carrier:string, page:integer, per_page:integer (1–50)"
      - name: "commerce_search_tickets"
        access: "read"
        requires: "desk.tickets.read"
        summary: "Search customer care tickets by text and filters (open by default); paginated."
        params: "query:string, scope:string, status:string, priority:string, tag:string, market_id:integer, client_id:integer, from:date, to:date, sla_breached:boolean, sort:string, direction:string, page:integer, per_page:integer (1–50, default 20)"
  - module: "Intelligence"
    prefix: "intelligence_"
    description: "Sales analytics of the company's Shopify store by period, channel, country and product, plus custom dashboards. Read-only; available to every Intelligence role. Periods use the shop's timezone."
    tools:
      - name: "intelligence_get_channel_report"
        access: "read"
        requires: null
        summary: "Full report of one sales channel for a period."
        params: "channel:string (required; ecommerce, retail, b2b, marketplace), marketplace:string, range, from, to, compare, compare_from, compare_to"
      - name: "intelligence_get_dashboard"
        access: "read"
        requires: null
        summary: "Read one custom dashboard with its widgets and metrics (private dashboards: owner or admins only)."
        params: "id:string (required; id or slug)"
      - name: "intelligence_get_kpi_summary"
        access: "read"
        requires: null
        summary: "Headline KPIs for a period with comparison and percentage change."
        params: "range, from, to, compare, compare_from, compare_to, channel:string, country:string (ISO-2), marketplace:string"
      - name: "intelligence_get_sales_by_country"
        access: "read"
        requires: null
        summary: "Sales by country sorted by revenue: orders, units, revenue, net, AOV, return rate, share."
        params: "range, from, to, channel:string, limit:integer (1–50, default 10)"
      - name: "intelligence_get_sales_overview"
        access: "read"
        requires: null
        summary: "Whole-business revenue by channel with comparison: the starting point."
        params: "range, from, to, compare, compare_from, compare_to"
      - name: "intelligence_get_sales_trend"
        access: "read"
        requires: null
        summary: "Sales time series by day, week or month, with an optional comparison series."
        params: "range, from, to, compare (default none), compare_from, compare_to, measures:string[] (max 7), channel:string, country:string, marketplace:string"
      - name: "intelligence_get_top_products"
        access: "read"
        requires: null
        summary: "Best-selling SKUs with units, orders, revenue, returns and rates."
        params: "range, from, to, channel:string, sort_by:string (revenue, units, returns), limit:integer (1–50, default 10)"
      - name: "intelligence_list_dashboards"
        access: "read"
        requires: null
        summary: "The company's custom dashboards with visibility and whether you can open each."
        params: "section:string"
