# Caroush MCP developer guide

Caroush MCP lets an authorized AI agent use the same Laravel services that power Caroush: products, content, carousels, captions, scheduling, publishing, generation automations and saved analytics. Each connection is bound to one user and one workspace. This guide documents the implemented API, not a list of planned endpoints.

The intended production resource is `https://mcp.caroush.com/mcp`, with `https://app.caroush.com` as the OAuth issuer. These addresses are configurable. Local installations should use the endpoint shown in **Settings → AI connections**. The public documentation route is `/developers/mcp`; a separate docs hostname can serve it at `/`.

**Protocol:** Laravel MCP 1.0.1, Streamable HTTP, JSON-RPC 2.0. The SDK supports MCP 2026-07-28 and legacy initialization versions including 2025-11-25 and 2025-06-18. It supports request-scoped streaming, not a permanent server event stream. GET and DELETE on `/mcp` return 405. There is no conventional `/api/generate_carousel` REST endpoint: invoke named tools through `tools/call`.

**Status:** implemented and tested locally; this change does not deploy the service, provision DNS, or validate production provider credentials. Real AI/provider calls require your configured account, permissions, credits and running workers. Test fixtures mock provider calls to avoid charging credits or publishing content during verification.

## Start here

1. An operator enables MCP, runs the additive migrations and configures persistent Passport signing keys. See `mcp-deployment.md` in the documentation download.
2. Add the remote endpoint in an MCP client supporting OAuth authorization code + PKCE and public client registration.
3. The client opens Caroush. Sign in, select one workspace and approve only the permissions needed.
4. Discover available tools. Call `get_profile`, `get_workspace` and `get_credit_balance` to confirm the context.
5. Read schemas before invoking tools. Mutations require a UUID `idempotency_key`.
6. Follow `approval_url` when returned. Queued requests and native generation runs must be polled for their actual outcome.

No shared Caroush API key is used. An agent never receives database, Stripe, AI provider or social-network credentials.

## Clients

### Codex

Codex supports remote Streamable HTTP servers and OAuth with DCR. Add this to your personal `~/.codex/config.toml`:

```toml
[mcp_servers.caroush]
url = "https://mcp.caroush.com/mcp"
```

Then run:

```sh
codex mcp login caroush
codex mcp list
```

The server permits variable ports on numeric loopback HTTP redirects (`127.0.0.1` and `[::1]`) as required by RFC 8252. The exact redirect used when issuing a code is still required when exchanging it. Never use `0.0.0.0` or a wildcard as a registered redirect.

Official reference: https://developers.openai.com/codex/mcp/

### Claude Code

```sh
claude mcp add --transport http caroush https://mcp.caroush.com/mcp
claude mcp login caroush
```

Use the browser consent flow when prompted. You can also open an interactive Claude Code session, run `/mcp`, select Caroush and authenticate; use this path if an older client does not support the `login` subcommand. Official reference: https://code.claude.com/docs/en/mcp

### Cursor

Add a remote server in Cursor MCP settings, or use its documented MCP JSON shape:

```json
{
  "mcpServers": {
    "caroush": { "url": "https://mcp.caroush.com/mcp" }
  }
}
```

Complete OAuth in the browser. Official reference: https://cursor.com/docs/mcp.md

### Other clients and VS Code agents

Use a current client that supports Streamable HTTP, PKCE S256, resource indicators and dynamic public-client registration. Supply the endpoint; let the client follow the `WWW-Authenticate` metadata rather than hard-coding token URLs. A client without compatible OAuth needs a compatible connector; this server does not provide an unrestricted static-token workaround. VS Code's native configuration format differs from Cursor's—consult its current MCP documentation rather than copying Cursor JSON into it.

## Authentication

OAuth is implemented with Laravel Passport 13.7.6 and League OAuth2 Server. Only public authorization-code clients and refresh tokens are exposed. Client credentials, password grants, implicit grants, personal access tokens, and browser session cookies cannot authenticate MCP requests.

| Route | Purpose | Authentication |
| --- | --- | --- |
| `GET /.well-known/oauth-protected-resource/mcp` | Canonical resource, issuer and scopes | Public, MCP enabled |
| `GET /.well-known/oauth-protected-resource` | Resource metadata alias | Public, MCP enabled |
| `GET /.well-known/oauth-authorization-server` | Authorization/token/revocation/registration metadata | Public, MCP enabled |
| `POST /mcp/oauth/register` | Register public client redirect URIs | Public, throttled |
| `GET /mcp/oauth/authorize` | Sign in and display workspace/scope consent | Browser session |
| `POST /mcp/oauth/authorize` | Approve or deny one-time consent | Browser session + CSRF |
| `POST /mcp/oauth/token` | Exchange code or rotate refresh token | PKCE or refresh credential |
| `POST /mcp/oauth/revoke` | Revoke the connection associated with a token | Possession of token + client ID |
| `POST /mcp` | MCP protocol | OAuth bearer token |
| `GET /settings/integrations/mcp` | Manage your connections and requests | Browser session |
| `POST /settings/integrations/mcp/connections/{uuid}/revoke` | Revoke one connection | Browser session + CSRF |
| `GET/POST /settings/integrations/mcp/requests/{uuid}` | Review/approve/decline an exact action | Owner session + CSRF for POST |

An unauthenticated MCP request returns HTTP 401 with a `WWW-Authenticate: Bearer` challenge containing `resource_metadata`. Register using the registration URL from issuer metadata. Registration accepts `client_name`, 1–10 `redirect_uris`, `token_endpoint_auth_method: "none"`, authorization-code/refresh grant types, and `response_types: ["code"]`. Redirects must be HTTPS, or HTTP on loopback, with no fragment, user info or wildcard. HTTPS redirects and the `localhost` hostname use exact matching; numeric HTTP loopback ports follow RFC 8252.

Example public-client registration:

```json
{
  "client_name": "My Caroush agent",
  "redirect_uris": ["http://127.0.0.1:8765/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
```

The client creates a cryptographically random PKCE verifier and its SHA-256 base64url challenge, then opens the authorization endpoint with `client_id`, `response_type=code`, registered `redirect_uri`, `resource` equal to the exact MCP endpoint, requested space-separated `scope`, `state`, `code_challenge`, and `code_challenge_method=S256`. The user chooses the workspace. Clients cannot supply a user/workspace override to tools.

Exchange the code as form data with `grant_type=authorization_code`, `client_id`, `redirect_uri`, `code`, `code_verifier` and the same `resource`. Access tokens last one hour. Refresh tokens last 30 days and rotate after use. A refresh uses `grant_type=refresh_token`, `client_id`, `refresh_token` and `resource`; optional `scope` can narrow access, never expand it. Store tokens using your client's credential store and replace rotated refresh tokens immediately. Reusing a code or an old refresh token is rejected.

A workspace-specific grant marker is embedded by the server. The server verifies it against the signed token, recorded grant, client, resource, account status and current membership. Changing the browser's selected workspace cannot retarget a token. Revoke one grant in Settings or call revocation with `token` and `client_id`; existing codes, access tokens and refresh tokens for that grant are invalidated. Queued MCP operations recheck authorization and the MCP enabled flag before executing. Native publishing/generation work already started is not retroactively undone by revocation; stop it through the relevant Caroush workflow if needed.

Dynamic Client Registration is supported for compatibility. Client ID Metadata Documents are not implemented or advertised. Consent is not inferred from arbitrary website content. State, one-time consent nonces, session authentication and CSRF protect the browser authorization flow.

## Permissions

The complete scope map is included with the catalog. Read permissions cover profile, the authorized workspace, connected social accounts, content, media, products, schedules, automations, analytics and jobs. Separate create/manage/delete permissions cover content, products, carousels, captions and automations; publishing and scheduling have distinct permissions.

Tool discovery exposes only the tools allowed by the current access token. An attempted known tool requiring another scope returns HTTP 403 with an OAuth `insufficient_scope` challenge. The user must authorize the additional access; the agent cannot grant it to itself.

`read:jobs` is needed to poll generic operation IDs. Polling also requires the original tool's permission and the same connection, user and workspace. Plan entitlements and credits remain enforced in addition to OAuth scopes. Analytics currently requires Pro. No MCP operation upgrades subscriptions, grants credits, changes provider credentials or disconnects social accounts.

## Protocol requests

Send `Authorization: Bearer <access-token>`, `Content-Type: application/json`, and `Accept: application/json, text/event-stream` to the resource endpoint. Do not put access tokens in URLs.

Legacy initialization, for clients negotiating 2025-11-25:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-11-25",
    "capabilities": {},
    "clientInfo": { "name": "caroush-example", "version": "1.0.0" }
  }
}
```

For subsequent legacy requests, include the negotiated `MCP-Protocol-Version` header. A simple tool request is:

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": { "name": "get_workspace", "arguments": {} }
}
```

The 2026-07-28 protocol carries request metadata. Include matching `MCP-Protocol-Version: 2026-07-28`, `Mcp-Method: tools/call` and `Mcp-Name: get_workspace` headers:

```json
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_workspace",
    "arguments": {},
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "caroush-example", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}
```

Use the official MCP client SDK's transport handling where possible. Discovery uses `tools/list`; the current protocol also supports `server/discover`. Follow pagination cursors returned by discovery instead of assuming every tool fits in one response.


To create a saved draft through the same API:

```json
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "create_text_post",
    "arguments": {
      "caption": "Small consistent steps make a useful routine.",
      "status": "draft",
      "idempotency_key": "33333333-3333-4333-8333-333333333333"
    }
  }
}
```

This uses the legacy envelope above. Generate your own UUID for a new action, and reuse it only when retrying that same action. For the current protocol, add the metadata and matching headers shown above. The result is a saved draft, not a published social post.

## Results and job states

Each tool returns MCP `content` and `structuredContent`. The structured envelope contains a server-generated `request_id`, `status`, and, when appropriate, `data`, `operation_id`, `approval_url` or `error`. The machine-readable catalog provides the full output schema for every tool.

Illustrative approval response; IDs are examples, not real resources:

```json
{
  "request_id": "11111111-1111-4111-8111-111111111111",
  "operation_id": "22222222-2222-4222-8222-222222222222",
  "status": "approval_required",
  "approval_url": "https://app.caroush.com/settings/integrations/mcp/requests/22222222-2222-4222-8222-222222222222"
}
```

| State | Meaning | Client action |
| --- | --- | --- |
| `approval_required` | Exact request recorded; effect has not run | Show the URL to the user; never approve on their behalf |
| `approved` / `queued` | User approved, or background request accepted | Poll `get_job_status` with `operation_id` |
| `running` | One worker owns execution | Wait; do not generate a new key |
| `succeeded` | Tool handler finished | Inspect `data` for native run or provider state |
| `failed` | Validation, access or application failure | Read error; correct the cause before a new intentional action |
| `dispatch_failed` | Queue dispatch failed | Retry the identical call with the same key; automatic recovery also runs |
| `denied` | User declined or approval expired | Stop; a new request requires renewed user intent |
| `outcome_unknown` | Worker stopped or an unexpected error prevented confirmation | Inspect existing records before any new request; no blind replay |

`status=succeeded` at the MCP envelope means the handler returned successfully. It does **not** promise AI generation, provider processing or publication finished. `generate_carousel`, regeneration and retry queue native jobs and return the actual post/run IDs. Poll `get_carousel` using its documented `post_id`. `run_automation` returns a native run: poll `get_automation_run` using `kind` and `run_id`. Publishing can remain pending; inspect returned schedules with `get_schedule`. Confirm completion from real statuses and `post_url` values, never invented permalinks.

`get_job_status` itself is a successful read, so the operation's state is inside its `data.status`. It is visible only to the connection that created it. Stored arguments and successful results are encrypted with the Laravel application key. Keep that key stable on redeployment.

## Idempotency and approvals

Every mutation includes a new UUID `idempotency_key` for each intentional action. Reuse the same UUID **and exactly the same arguments** for retries. The unique key is scoped to connection + tool. Changing arguments for the same key returns `idempotency_conflict`. A new connection is a new idempotency namespace: reconcile previous content before repeating an action after reconnection.

Publishing, scheduling, deleting, approving a carousel, and running/changing/enabling/disabling automations require a separate browser review. The page displays the immutable request, owned resource labels and destinations. Requests expire after 30 minutes. An approved action executes once under its saved effective scopes, rechecking current grant and membership. A second approval cannot run it twice.

Creating a generation automation saves a draft; it does not start publishing. An active automation can affect future posts, so updates also require review. Creating draft posts, product analysis or credit-consuming carousel generation uses the user's approved OAuth scopes directly; those scopes should be granted only to agents the user trusts.

The scheduled `caroush:mcp-maintain` command runs every five minutes. It expires approvals, redispatches operations stranded before execution for more than ten minutes, and marks overdue running operations as unknown. Execution claims are atomic; duplicate queue delivery cannot start the same handler twice. Recovery never blindly replays a handler already marked running or finished.

## Supported tools and workflows

The **Tool reference** below and `catalog.json` are generated directly from `ToolCatalog`. Each entry includes its strict input schema, structured output schema, required scope, read/write behavior, browser approval and background-handler flag. Unknown arguments are rejected. `queued_handler=false` does not imply a finished output: some fast handlers enqueue the existing native job themselves.

Implemented groups:

- Account: identity, authorized workspace, credits, plan/quota settings and operation status.
- Products: list/get/create/update/delete, safe URL detail fetching and stored product assets.
- Content and search: bounded listing/search, drafts, text creation, image-post creation from existing owned assets, reading and deletion.
- AI: real caption/hook/hashtag variations and UGC on-screen hook generation through the shared text services.
- Carousels: available design systems, full generation, saved outline, current results, targeted regeneration, checkpoint retry and human approval.
- Media: list/search/get saved owned images and videos. An asset ID refers to the database asset, not a filesystem path.
- Social and schedules: safe connected-account list, delivery records, publishing and scheduling existing content.
- Automations: draft/create/update/enable/disable/delete/run, configuration and run history for carousel and video generation, including 2×2, single fade and AI UGC.
- Analytics: real saved account/post metrics and highest-viewed content from Caroush-published deliveries.

### Carousel to LinkedIn

1. `list_products` → choose a product; `list_carousel_formats` → choose an available format.
2. `generate_carousel` with the product ID, supported slide count, design and a new idempotency UUID.
3. Poll `get_carousel` until generation is complete. Read the generated slides and copy.
4. Request `approve_carousel`; the user reviews and approves it in Caroush.
5. Use `list_connected_social_accounts` to select the owned LinkedIn destination.
6. `schedule_post` with the existing post ID and explicit timestamp. The user reviews the exact destination and date; poll the returned operation and schedule.

Carousel generation follows the saved product's niche and editorial profile. A generic prompt without a product does not bypass product selection. Use the selected tool's schema for exact field names, limits and enum values.

### Learn from performance

Use `get_account_analytics` and `get_best_performing_content`, then read relevant saved posts. The agent can reason over these results and draft fresh captions with `generate_captions`. It can create a real new carousel from the existing product. Do not call invented campaign or content-variation tools.

### Daily UGC or carousel automation

Create a draft with `create_automation`. Choose `kind=video` or `kind=carousel` and the actual configuration options in the schema. For video, `configuration.type` is `ugc-video`, `grid-video` or `fade-video`; the corresponding `template_id` values are `ugc-video`, `2x2-grid` and `single-fade`. UGC requires an owned stored 3–15-second CTA video; use `cta_asset_id` rather than a URL or local path. Presenter and library-pool management remains in Studio; reuse valid configured IDs or inspect an existing automation with `get_automation` rather than inventing IDs. Then request `enable_automation` or one `run_automation`. The user approves it, and native pipelines enforce credits, subscription, presenter, CTA, social-account and media rules.

These generation automations support daily configured times and manual occurrences, not arbitrary cron/RSS/webhook/event graphs. Existing legacy folder scheduling is a different system; it is not silently translated to new generation definitions.

## Credits and plan limits

MCP reuses the existing credit ledger and entitlement services. Reading does not consume generation credits. Generated images cost 5 credits each: 6 newly generated carousel images cost 30, a 4-image 2×2 video costs 20, and a single-fade image costs 5. Uploaded carousel CTA artwork replaces the final generated slide, so a 6-slide carousel with a stored CTA generates 5 images for 25 credits. AI UGC automation costs 10 credits and requires a CTA video.

Targeted carousel image/slide regeneration follows the native pricing rules. Copy, hook and layout operations reuse existing images where supported. Failed-generation refunds and retry recharges follow the existing ledger; clients must not infer a refund just from a failed MCP response. Read the current balance and returned `credits_charged` values.

Creator currently permits 2 owned workspaces and 2 automations; Growth permits unlimited workspaces and 6 automations; Pro permits unlimited workspaces and automations. Automation slots are shared across legacy posting folders, video and carousel definitions in every workspace, including saved drafts; they are not a separate allowance for each social account. These are enforced by current account-wide entitlement code; use `get_account_settings` for live quotas. Social destination account limits and all other entitlements remain those of the existing plan. A scope does not bypass a subscription or credit requirement.

## Analytics and social platform limits

Analytics tools read stored real metrics only for content published through Caroush. They never fetch providers on page/tool reads and never fabricate unsupported metrics. Daily provider synchronization remains at midnight America/New_York through the existing scheduler; an operator/user can still use the normal manual-sync flow. Coverage, timestamps and unavailable metrics distinguish missing data from zero.

Date windows select a **publication cohort**, while saved counters are lifetime metrics as of the last sync. They are not daily incremental views. Top-content ranking uses available views, up to five items; platforms define views differently. Comparisons should acknowledge different metric definitions and data coverage.

Actual integrated providers are Instagram, Facebook, TikTok, LinkedIn, X/Twitter, Threads and Bluesky. Valid content, publishing permissions, external app reviews, provider limits and public-media access still apply. Direct TikTok publishing and scheduling through MCP are rejected: TikTok requires creator options and per-post consent in Caroush's review UI. Automation-generated TikTok output continues into that review flow. The agent cannot manufacture those acknowledgements.

The Instagram API does not provide a general music-library attachment workflow for photo/carousel posts here. MCP does not claim to add licensed Instagram catalog music.

## Errors and rate limits

JSON-RPC validation/unknown-method errors use MCP protocol error objects. Domain failures use `isError=true` and the structured `error` object. A response can be HTTP 200 while the MCP tool result is an error; inspect both layers. Authentication, missing scope, Origin, transport and middleware rate failures use HTTP status codes.

Common domain codes include `invalid_arguments`, `validation_failed`, `not_found`, `insufficient_scope`, `forbidden`, `account_setup_required`, `integration_disabled`, `idempotency_conflict`, `rate_limited`, `internal_error` and `outcome_unknown`. Errors include a request ID; quote it when asking for support. Do not log bearer tokens or full prompts in client telemetry.

| Budget | Limit per user per minute |
| --- | ---: |
| Reads | 120 |
| Normal writes | 30 |
| AI/credit-generation actions | 5 |
| Publishing/approval-gated actions | 5 |
| Deletions | 3 |

Additionally the HTTP MCP endpoint is limited to 240 requests per IP per minute, OAuth routes to 60, registration to 10, and browser consent/settings to 30. The docs ZIP endpoint is limited to 5 downloads per minute. Use backoff and jitter; observe the returned `retry_after` detail or HTTP `Retry-After` header. Poll gradually (for example, 2 seconds initially then 5–10 seconds), not every animation frame. Every mutation, including validation failures, is bounded by the input schema; protocol bodies are limited to 256 KiB.

Limits are configured in `config/caroush_mcp.php`; shared cache/Redis makes them consistent across workers. The limiter is shared per user, not per freshly registered client, to discourage bypass by creating new clients.

## Security and data handling

- No MCP session-cookie fallback. OAuth bearer validation checks the signed token and live database authorization.
- Every resource is scoped to both its creator and consented workspace. Merely being a member of a shared workspace does not grant another member's records through MCP.
- ID fields, shapes, enum values, lengths and quotas are validated. No arbitrary SQL, filesystem paths or mass-assignment payloads are accepted.
- Media reuse accepts owned stored asset IDs. Product URL extraction reuses Caroush's guarded fetch path; there is no arbitrary remote media downloader.
- Social-account responses use explicit projections; decrypted tokens and provider metadata are not serialized.
- Treat retrieved captions, descriptions, URLs and imported website text as untrusted data. They cannot change OAuth scope or satisfy browser approval.
- Protocol Origin validation and explicit CORS allowlists protect browser access. No wildcard credentialed CORS. Native/server clients normally send no Origin.
- CSRF applies to browser consent, grant revocation and action approval. Sessionless bearer/token endpoints do not use browser CSRF cookies.
- Arguments/results are encrypted at rest in operations. OAuth access records contain identifiers and revocation state, not reusable plaintext bearer tokens. Refresh tokens/codes are protected by Passport's cryptographic flow.
- MCP audit records store request/user/workspace/grant/operation identifiers, tool, controlled argument **names**, timing, status and error code. They do not store argument values, prompts, captions, media URLs or secrets.
- Existing Sentry handles unexpected errors. MCP request bodies, query tokens, results, free-form exception text and credential-bearing context are filtered before telemetry; useful codes, IDs and stack locations remain.

The public catalog is documentation of capabilities, not authorization. A tool listed publicly still requires a valid token, the proper scope, ownership, entitlements and any approval before execution.

## Not exposed as MCP tools

The audit found no complete persisted and charged standalone image-generation workflow, campaign CRUD, generic article-to-thread repurposing engine, standalone trending-topic service, or optimal-posting-time algorithm. Those capabilities are not invented here. Image generation is available through the existing carousel and video-automation pipelines.

Standalone manual video rendering/export, free-form media upload/delete, social-account connection/disconnection, general post edit/duplicate, schedule cancel/reschedule, billing changes, provider-key configuration, and legacy folder automation management remain in the web application. Some exist in the UI but need additional upload/review/lifecycle contracts before safe remote exposure. They are deliberately documented as unavailable through this MCP version, rather than represented by mock tools.

Carousel outline access reads a saved pipeline plan; it is not a separate AI outline-generation job. General content ideas are part of the existing carousel/automation engine, not an independent ideas API. Multi-day campaigns can be organized manually from supported draft/schedule tools, but there is no `create_campaign` or invented campaign ID.

## Local development and deployment

Read the included `mcp-architecture.md` and `mcp-deployment.md` for the service map, schema migration details, worker topology, environment variables, keys, domain routing and Coolify instructions. The integration runs inside the existing Laravel container and reuses its workers. There is no separate MCP daemon.

Public documentation endpoints:

- `/developers/mcp` — guide and searchable complete tool reference.
- `/developers/mcp/catalog.json` — machine-readable tool schemas and capabilities.
- `/developers/mcp/guide.md` — Markdown guide.
- `/developers/mcp/download.zip` — portable documentation website, schemas and operator guides.

Export the same bundle locally:

```sh
php artisan caroush:mcp-docs
```

The command prints the private output path. Unzip it and open `index.html`, or host the directory on `api.caroush.com`. No Node build is required for the static documentation. To serve it through this Laravel deployment instead, set `CAROUSH_API_DOCS_URL=https://api.caroush.com`, configure DNS/TLS and add that hostname to the app in Coolify. The root on that hostname serves the public documentation, while the normal application homepage remains unchanged.

After tool changes, regenerate the bundle. The live website and catalog always read current definitions. The ZIP contains no environment files, API keys, database records or connected-account data.

## Troubleshooting

- **404 on `/mcp` or OAuth metadata:** enable MCP, clear/rebuild config cache, verify host matches the configured resource, and deploy the correct source.
- **401 / invalid token:** check exact resource, token expiry, signing keys, account status, grant revocation and workspace membership; reconnect if needed.
- **403 / insufficient scope:** authorize the permission in a new client consent flow. `read:jobs` alone is not permission to see another tool's results.
- **Redirect mismatch:** use an exactly registered HTTPS/localhost URI, or the numeric-loopback RFC 8252 rule. Code exchange must match the URI used during authorization.
- **Invalid keys / JWT signing failure:** install persistent matching Passport keys and rebuild runtime config. Never regenerate APP_KEY or signing keys at every start.
- **Approval page unavailable:** open it as the same signed-in user and check the original grant is still active. Requests are not shareable between users.
- **Queued forever:** check the database/caroush worker and scheduler; ensure queue retry_after exceeds 1800 seconds. Inspect the request ID and existing native run before retrying.
- **Result succeeded but image missing:** inspect native generation status, not just the MCP handler envelope. Workers/providers may still be processing.
- **Unknown outcome:** reconcile delivery records or generated content in Caroush first. A new idempotency key can create a second intentional action.
- **Browser CORS error:** add only the actual trusted client origin to `CAROUSH_MCP_ALLOWED_ORIGINS`; check proxy forwards OPTIONS and the required MCP headers.
- **No analytics:** check Pro entitlement, Caroush-published delivery status, provider scopes, sync timestamps and coverage. Missing metrics are not automatically zero.
- **TikTok rejects MCP publication:** open the generated content in Caroush and complete the official per-post creator review flow.

## References

- Laravel MCP: https://laravel.com/docs/13.x/mcp
- Laravel MCP release: https://github.com/laravel/mcp/tree/v1.0.1
- MCP authorization: https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization
- MCP Streamable HTTP: https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http
- Codex: https://developers.openai.com/codex/mcp/
- Claude Code: https://code.claude.com/docs/en/mcp
- Cursor: https://cursor.com/docs/mcp.md
