# Extuitive > Extuitive bulk-uploads Meta (Facebook / Instagram) ad creative - both images and videos - through an AI agent skill and a remote MCP server. Uploads go into an organization's shared library; requests address each connected Facebook ads account by the `workspaceId` that `list_ad_accounts` gives it, and people know it by its name and `act_…` id. Install with a single command into Claude Code, Codex, or Claude Desktop; any other MCP host connects to the same URL with OAuth. This file is for AI agents. If a person asked you to set up Extuitive, install the skill, or connect the Extuitive MCP server, follow the steps below exactly. Do not invent setup commands - they differ per host. ## Install (one command) ```bash npx extuitive install ``` - Published on npm as `extuitive`. `npx github:fl100inc/extuitive-skill install` runs the unreleased `main` instead. - Requires Node.js 20 or newer. - With no `--host`, the installer detects which hosts are on the machine and asks. Add `--host ` to skip the prompt. Add `--yes` to take defaults without prompting (required when there is no TTY). - It copies the skill to where the host looks for it, registers the MCP server, and prints how to sign in. It never handles credentials. - No Extuitive account is needed first. Sign-up happens in the browser during the OAuth step (one-time email code, Google, or Facebook). Connecting a Meta ad account is a separate, later step done from the client: `list_ad_accounts` returns the link when the person asks for Meta or needs an ad account. ## Per-host details ### Claude Code (the `claude` CLI, and the Code tab of the Claude Desktop app) ```bash npx extuitive install --host claude ``` Equivalent to: copying the skill to `~/.claude/skills/extuitive` and running `claude mcp add --transport http extuitive https://www.extuitive.com/mcp --scope user`. Then: 1. Start a NEW Claude Code session. Servers connect at startup, so `extuitive` is not visible in the session that installed it. 2. In the new session run `/mcp`, choose `extuitive`, and approve in the browser. 3. Invoke as `/extuitive `, e.g. `/extuitive init`, `/extuitive upload ./creative`. ### Codex (CLI, desktop app, and IDE extension share one install) ```bash npx extuitive install --host codex ``` Equivalent to: copying the skill to `~/.codex/skills/extuitive` (`$CODEX_HOME/skills`; a copy found at the old `~/.agents/skills/extuitive` path is migrated), and running `codex mcp add extuitive --url https://www.extuitive.com/mcp`. Then: 1. Run `codex mcp login extuitive` and approve in the browser. 2. Restart Codex. It reads skills once at startup. 3. Invoke as `$extuitive `, e.g. `$extuitive init`. Codex reserves `/` for built-ins, so `/extuitive` returns "Unrecognized command" even when the install is correct. ### Claude Desktop (Chat and Cowork tabs) ```bash npx extuitive install --host claude-desktop ``` This builds `~/.extuitive-skill/bundles/extuitive.zip`. Nothing can be registered automatically; the person then does, in the app: 1. Settings > Capabilities: turn on code execution and file creation. 2. Customize > Skills: `+` > Create skill > Upload a skill > choose `extuitive.zip`. 3. Settings > Connectors > Add custom connector, URL `https://www.extuitive.com/mcp`. 4. Approve access in the browser, then start a new chat. There is no invocation prefix in Claude Desktop; the skill matches on the request ("upload these ads to Extuitive"). Do not put the URL in `claude_desktop_config.json` - that file only accepts stdio servers and the app will strip the whole `mcpServers` block on next launch. ### Any other MCP host (Cursor, ChatGPT connectors, claude.ai, etc.) Add a remote Streamable HTTP MCP server with no client id, secret, or API key: - URL: `https://www.extuitive.com/mcp` - Transport: Streamable HTTP, JSON-RPC over POST - Auth: OAuth 2.1 authorization code + PKCE (S256), Dynamic Client Registration (RFC 7591), scope `mcp` - Discovery: `https://www.extuitive.com/.well-known/oauth-protected-resource` and `https://www.extuitive.com/.well-known/oauth-authorization-server` Example Cursor `~/.cursor/mcp.json` entry: ```json { "mcpServers": { "extuitive": { "url": "https://www.extuitive.com/mcp" } } } ``` Hosts without the skill still get every tool; the skill only adds the guided `init | select | upload | upload-status | connect` flows and the local upload script. ## After installing - Sign-in is a browser step only the person can complete. If you are an agent running inside a session, tell them which step to do (`/mcp` in Claude Code, `codex mcp login extuitive` in a terminal, Connect in Claude Desktop's Settings > Connectors) rather than retrying. - Tools listed but every call refused = sign-in was never completed. - The skill's five commands: `init` (set up from scratch), `select` (choose which ad account this session works in), `upload` (upload a folder of images or videos), `upload-status` (how the current upload is going), `connect` (connect Meta or repair a stale ad account connection). - Diagnose with `npx extuitive doctor` (add `--json` for machine-readable output). Update with `... update`, remove with `... uninstall` (`--keep-server` keeps the MCP registration). ## How the tools work - Everything belongs to an organization: its uploads, share links, library and brand research are shared with everyone in it. A connection works in one organization, chosen when the person connected: every tool works in it and no other, and none takes an `organizationId`. `list_organizations` names it. To work in another organization, the person reconnects Extuitive and chooses it. - Rows say who added them, as a user id: `uploadedBy` on uploads, `requestedBy` on brand research. To see one person's, pass `"me"` or a `userId` from `list_organization_members` as `uploadedBy` to `list_upload_batches` / `list_upload_content`, or as `requestedBy` to `get_brand_research` with no `jobId`. - Tools that act in a Meta ad account take its `workspaceId` from `list_ad_accounts`, which lists only this organization's. The `workspaceId` is for requests only: when talking to the person, name an ad account by its name and `act_…` id, never by its `workspaceId`, and never call it a workspace. If `list_ad_accounts` is empty, it returns the setup link: give it to the person when they ask to connect Meta or need an ad account — not before. If an ad account reports `metaConnection.canReconnect` (token expired, revoked or missing permissions), its `metaConnection.reconnect` gives its token holder the fix; offer it when a tool on that ad account fails with `meta_token_invalid` / `meta_not_connected` or they ask. - `list_workspaces` is deprecated (use `list_ad_accounts`), and so are a `workspaceId` on the tools that work across the organization rather than in one ad account and the older tool names the tools below replaced; all are still accepted until 2026-10-23, and results from them carry a `deprecated` notice naming the replacement. - When the person asks to upload image or video files, or shares them while talking about their ads, Meta or Extuitive, offer to upload them with Extuitive ("Do you want to upload these files with Extuitive?"). Videos are accepted just like images, and one batch can hold both. - MCP tools never carry file bytes. `create_upload_batch` returns presigned storage destinations plus a server-signed `plan`, the hosted `uploader` (`/mcp/uploader/v4.mjs`, Node 20+) that executes it, and `nextActions`. Every upload result carries `nextActions`; follow them in order. - Capability test before opening a batch: direct upload only if you can read each file's bytes from its local path AND run the uploader with Node 20+ (without Node, a file qualifies only if you send it yourself: a `PUT` destination with its headers, a `MULTIPART` one through `sign_upload_parts`, a PUT per part with its signed headers only, and `complete_upload`). A shell without access to the attachment's path does not qualify. - If `create_upload_batch` refuses the manifest, the refusal carries the workspace's upload `limits`; fix the list and retry. - Direct path: write `plan.json` as `{ batch: , files: [{ contentId, localPath }] }` (never edit the plan; `localPath` is the only field you add; match by `contentId`), then `node extuitive-upload.mjs plan.json`. It sends every file, multipart included, waits for each to be accepted or rejected, and prints one JSON report within `--wait-seconds` (default 100). Exit `0`: every file `READY`; `1`: a file was `REJECTED` or failed; `2`: the plan was refused; `3`: still in progress, run it again (finished work is skipped). Delete `plan.json` and any checkpoint files afterwards. - Files that fail the test: an owner or admin of the organization calls `create_upload_share_link` once for them and you show the returned `url` in the conversation verbatim, then poll `get_upload_share_link` until `terminal`. There is no other page to send anyone to. - Without the uploader, poll `list_upload_content` with the `batchId` and `summaryOnly: true` until the batch is `settled`, `suggestedPollSeconds` apart; a host that cannot pause passes `waitSeconds` and the server waits for it. `READY` is the only status that means a file was accepted; `VALIDATING` means still being checked. - Uploads land in the organization's library, not in any Meta ad account. A file reaches an ad account only through `publishToWorkspaceIds` on `create_upload_batch` or a later `publish_to_meta`; ask the person first. Its image hash or video id for ads there is on `metaPublishSummary.forWorkspace` of a row read with that `workspaceId`, once `PUBLISHED`. - Tool groups: organizations and ad accounts (`list_organizations`, `list_organization_members`, `list_ad_accounts`, and the deprecated `list_workspaces`), uploads (`create_upload_batch`, `sign_upload_parts`, `complete_upload`, `list_upload_batches`, `list_upload_content`, `get_upload_content`, `publish_to_meta`), share links (`create_upload_share_link`, `send_upload_share_link_email`, `get_upload_share_link`, `revoke_upload_share_link`), content library (`describe_content`, `group_aspect_ratio_variants`, `search_content`), creative insights (`get_creative_insights`), Meta objects (`create_meta_campaign`, `create_meta_adset`, `create_meta_adcreative`, `create_meta_ad`, `update_meta_object`, `get_meta_actions`, `list_meta_objects`, `get_meta_ad_config`, `get_meta_ad_identities`, `get_meta_insights`, `list_meta_audiences`, `get_meta_audience`, `search_meta_targeting`, `estimate_meta_reach`, `create_meta_custom_audience`), brand research (`start_brand_research`, `get_brand_research`), and support (`contact_support`). - Ad creatives: `create_meta_adcreative` sends every Advantage+ creative enhancement, multi-advertiser ads and personalized destinations as `OPT_OUT` unless the call opts in. The agent asks the person once per request whether they want any of them on, and opts in only what they confirm. If the person is unsure, the agent can read which enhancements their recent ads use (`list_meta_objects` with `includeCreative` and `includeEnhancements`, off by default because Meta lists every feature) and suggest those. - Renaming: for every connection, `update_meta_object` renames a campaign, ad set or ad (`params.name`, sent on its own). A rename runs at once, needs no approval and changes nothing about delivery or spend. - Live ads (offered to some organizations for now; only where it is enabled does `update_meta_object` take `status` and budgets, and the server accept `status: "ACTIVE"` on `create_meta_ad`, and on `create_meta_campaign` or `create_meta_adset` without a budget of its own). A campaign or ad set that holds its own budget is always created `PAUSED`; when the person asks, `update_meta_object` turns a campaign, ad set or ad on or off, or changes a campaign's or ad set's budget. To build something live, the agent creates the campaign `ACTIVE`, the ad sets `PAUSED` and the ads `ACTIVE` when the ad sets hold the budget, or the campaign `PAUSED` and the ad sets and ads `ACTIVE` when the campaign holds it; once every create has settled, it turns on what holds the budget with `update_meta_object`, and if any create failed it turns nothing on. An ad delivers only while it, its ad set and its campaign are all `ACTIVE`: a result names any paused parent in `delivery.blockedBy`, and the agent tells the person instead of turning the parent on. A budget change, or turning on a campaign or ad set that holds a budget, waits at `PENDING_APPROVAL` until someone approves it on the ad account's approvals page in Extuitive, which the result links as `approval.url`. A paused campaign whose ad sets hold their own budgets is never turned on here: the refusal, `turn_on_in_ads_manager`, says why and links the campaign in Ads Manager (`adsManagerUrl`), and that campaign's `delivery.blockedBy` entry carries the same reason as `adsManagerReason`. Where live ads are not enabled, everything is created `PAUSED` and the person turns it on in Ads Manager. - Brand research (offered to some organizations for now; the server lists these tools only where it is enabled) is consumer research for a brand, product line or product: a scanned brand site, verified customer quotes, competitor ads as execution references, rendered as an HTML deck and a PDF with the evidence kept alongside. It is free, takes no ad account and needs no Meta connection; it is shared with the organization and cached per organization, brand domain and scope. A new run takes 20 to 40 minutes, so call `get_brand_research` with no `jobId` first (it lists past runs; starting a run already in flight returns it with `inFlight: true`) and never pass `refresh: true` without the person asking for fresh research. Poll `get_brand_research` at `suggestedPollSeconds` until `terminal`; the artifact links are presigned and expire. - `contact_support` emails the Extuitive support team; the server adds who the person and organization are, so nothing about them goes in the arguments. `kind` says what is being reported (`bug`, `friction`, `feature_request`, `question`) and `initiatedBy` says who decided to file (`person`, `agent`). File a `bug` or `friction` yourself when a tool fails in a way the person cannot fix or works but is confusing or slow, once per distinct problem, and tell the person in one line; file a `feature_request` or `question` only when the person wants it sent. Limited to 5 reports per person per hour. ## Docs - [Setup guide for people](https://www.extuitive.com/support): Claude Desktop and Codex steps, what happens after an upload, and troubleshooting, written for the person you are helping - [Installer and skill source](https://github.com/fl100inc/extuitive-skill): README with every install flag, per-host behaviour, and troubleshooting - [SKILL.md](https://github.com/fl100inc/extuitive-skill/blob/main/skills/extuitive/SKILL.md): what the installed skill tells the agent - [Tool reference](https://github.com/fl100inc/extuitive-skill/blob/main/skills/extuitive/references/tools.md): full tool schemas, error vocabulary, and status lifecycle - [MCP endpoint](https://www.extuitive.com/mcp): Streamable HTTP, OAuth 2.1 + PKCE + DCR - [OAuth protected resource metadata](https://www.extuitive.com/.well-known/oauth-protected-resource): RFC 9728 - [OAuth authorization server metadata](https://www.extuitive.com/.well-known/oauth-authorization-server): RFC 8414 ## Optional - [Website](https://www.extuitive.com): marketing home and sign-in - [Terms](https://www.extuitive.com/terms) - [Privacy policy](https://www.extuitive.com/privacy) - [Data deletion](https://www.extuitive.com/data-deletion): disconnect Meta and request deletion of your data - Support: support@extuitive.com