Resources
Architecture & implementation
How the MCP layer connects to the Caroush application, and where each part of the implementation lives.
This guide describes the implementation in this repository. It does not mean the feature has been deployed or enabled on the live domains. Read the main guide for client setup and deployment instructions before enabling it.
Application architecture found in the audit
| Area | Existing implementation |
|---|---|
| Framework/runtime | Laravel 13.14.0; PHP ^8.3; PHP 8.3 in the production Dockerfile |
| Web application | React/Inertia served by Laravel; native application routes are in the caroush.native branch of routes/console.php |
| Existing authentication | Laravel user authentication, session-based browser login and OAuth social account connections |
| Accounts/workspaces | User, Workspace, workspace_user membership and a browser-selected workspace |
| Subscription and credits | Stripe subscription records, SubscriptionEntitlements, Billing, CreditLedger, CreditPricing; existing plan and credit checks remain authoritative |
| Products | Saved product/editorial profiles, URL analysis and reusable audience context |
| Carousels | Carousel, CarouselContent, CarouselEngine, layered strategies, format registry, image rendering, approval and regeneration |
| Manual content | Shared PostContent supports the existing text and stored/uploaded-media composer flows |
| Social publishing | Native PostScheduling → Publishing → SocialPublisherManager; durable delivery rows and provider receipts |
| Generation automations | Separate video and carousel definition/run services, snapshots, jobs, leases, credit reservations and publication state |
| Posting folders | Legacy queue folders publish existing approved drafts at configured daily/weekly slots |
| Analytics | Saved platform-specific observations and snapshots for Caroush-published deliveries; daily sync jobs |
| Storage | Existing Laravel filesystem abstraction, including configured Google Cloud Storage; stored files remain outside MCP filesystem access |
| Background processing | Database queues for general work, video automation, carousel automation and mail; shared cache locks; Redis can provide configured cache/session services |
| Webhooks and mail | Existing Stripe/social/Telegram handling and transactional mail delivery remain separate from MCP |
| Monitoring | Existing Laravel log stack and Sentry; Mixpanel remains the separate consent-gated product analytics pipeline |
Older controllers and migration-era provider code still exist in the repository. The MCP integration uses the native services. In particular, it does not publish through the older PublishScheduledPostJob, which has legacy billing behavior.
Protocol and request flow
The remote endpoint runs inside Laravel using laravel/mcp 1.0.1, the official Laravel MCP package. Protocol transport, JSON-RPC messages, initialization, version negotiation and tool discovery come from that package. Caroush does not implement a look-alike REST replacement for MCP.
- MCP client
- Laravel MCP HTTP endpoint
- Passport token and workspace grant checks
- Tool catalog, schemas and scopes
- Shared Caroush services
- PostgreSQL, filesystem and existing providers
View original Mermaid diagram
flowchart TD
Agent[MCP client] --> Endpoint[Laravel MCP HTTP endpoint]
Endpoint --> Auth[Passport token and workspace grant checks]
Auth --> Tools[Tool catalog, schemas and scopes]
Tools --> Read[Immediate read]
Tools --> Operation[Durable mutation operation]
Operation --> Approval[Browser approval when required]
Approval --> Queue[Existing database queue]
Operation --> Queue
Read --> Domain[Shared Caroush services]
Queue --> Domain
Domain --> Data[PostgreSQL, filesystem and existing providers]The principal components are:
| File/group | Responsibility |
|---|---|
routes/mcp.php | Protocol, metadata, OAuth, connection-management and documentation routes |
config/caroush_mcp.php | Enable flag, resource URL, issuer, documentation URL, scopes, limits and queue configuration |
app/Mcp/CaroushServer.php | SDK server and scope-filtered tool discovery |
app/Mcp/CaroushTool.php | SDK tool adapter |
app/Mcp/ToolCatalog.php | Combines domain definitions and adds request idempotency requirements |
app/Mcp/SchemaValidator.php | Validates strict JSON input and documented output schemas |
app/Mcp/McpContext.php | Authenticated user, immutable authorized workspace, grant, client and current operation context |
app/Mcp/Domains/* | Small adapters to existing account/content/operations services |
app/Mcp/OperationService.php | Operation ledger, replay handling, approval and result persistence |
app/Jobs/ExecuteMcpOperation.php | Asynchronous work with authorization rechecked at execution |
app/Mcp/Auth/* | Passport/League OAuth grant handling and Caroush workspace authorizations |
app/Mcp/Audit.php | Request metadata and argument names, excluding argument values |
app/Http/Controllers/Mcp/* | OAuth browser consent, connection management and documentation |
Authentication and workspace isolation
OAuth uses Passport 13.7.6 and League OAuth2 Server 9.4.1. Supported grants are authorization code with PKCE S256 and refresh token. The current public-client registration flow uses token_endpoint_auth_method=none; it does not issue a shared Caroush API key or a client secret for desktop agents.
- The client reads protected-resource metadata and authorization-server metadata.
- It registers callback URLs when dynamic registration is enabled. HTTPS and
localhostredirects use exact matching; numeric HTTP loopback redirects allow variable ports under RFC 8252. Code exchange still requires the exact redirect used during authorization. - It sends the advertised resource URL, requested scopes and a PKCE challenge to the authorization endpoint.
- The user signs into the existing Caroush browser session, chooses a workspace and approves scopes.
- The client exchanges the authorization code with the PKCE verifier and matching resource URL.
- Subsequent MCP calls use the access token in the
Authorizationheader.
Access tokens last one hour. Refresh tokens last 30 days and rotate after use. Workspace grants retain the user, registered client, approved scopes and exact resource URL. Revocation invalidates the grant and its associated access tokens, refresh tokens and unused authorization codes.
Account::useWorkspace() pins request-local service context. It does not alter users.current_workspace_id, so changing the browser workspace cannot silently move an MCP connection. Membership is rechecked, including when an approved background action starts. Nested records are authorized through their parent post or carousel run. MCP-owned data checks constrain both creator and workspace, including when another user's record is located in the same workspace.
Raw social-account models are never public results: their encrypted casts decrypt credentials when accessed, so serialization requires an explicit allowlist. Tool outputs omit social tokens, refresh tokens, internal provider configuration, claim tokens, raw generation snapshots and storage descriptors.
Shared services rather than duplicate implementations
The integration extracts existing controller behavior into these application services:
PostContent: existing content creation and deletion behavior.CarouselContent: existing carousel creation/review operations.PostScheduling: composer validation, destination checks, approval checks, TikTok consent validation, duplicate prevention and native publication orchestration.AutomationManagement: list/run history plus lifecycle actions shared with the browser. Pausing remains possible when a product or account disappears. Deleting an automation cancels unfinished work and preserves existing publication safeguards and refunds.SocialOperations: owner-scoped connected-account and delivery DTOs.StoredMediaUpload: converts an owned stored media asset into a bounded temporary upload for existing CTA validators; it accepts an asset ID, never a local path or arbitrary remote URL.
Video/carousel automation saves still use their original AutomationService::save() methods. Native run requests still use Scheduler::runNow() and the same UUID occurrence ledger as the web application. Existing browser controller responses are preserved by thin service delegates.
Operations, idempotency and approval
Every mutating tool requires a UUID idempotency_key. The operation ledger is unique for authorization, tool name and key. Repeating the same request returns its existing operation; reusing the key with different arguments returns idempotency_conflict.
Publishing, scheduling, automation activation/changes/runs and deletion require approval through the authenticated Caroush browser. A tool call initially returns approval_required with an approval_url. The browser reviews the stored request; the agent cannot manufacture approval by adding approved=true or inserting instructions into content. Approval requests expire after 30 minutes by default.
Approval permits only that stored action. The queue rechecks the grant, membership and required scope before execution. If permission was revoked while work waited, the action fails without receiving new authority from the old approval.
MCP operation IDs and native automation run IDs have different meanings:
| Identifier | Meaning |
|---|---|
| MCP operation UUID | Agent request/approval/background execution record; poll get_job_status |
| Generated post ID | Saved content that can be reviewed or delivered to platforms |
| Carousel run ID | Existing carousel generation/planning execution |
Automation ID plus kind | Saved video or carousel definition; their numeric IDs can overlap |
Automation run ID plus kind | One native occurrence; poll get_automation_run |
| Schedule ID | One delivery to one connected account; read get_schedule |
A successful queued request is not evidence that the resulting content exists or was published. A completed run_automation MCP request can mean its native run was accepted; continue polling the returned native run. Delivery status and confirmed platform receipts determine publication success.
If an unexpected worker failure could have happened after an external effect, the operation is marked outcome_unknown. Do not generate a fresh key and blindly retry it. Inspect Caroush and the destination platform first.
Actual social and automation capabilities
Existing publisher implementations cover Instagram, TikTok, Facebook, LinkedIn, X/Twitter, Threads and Bluesky. Platform permissions, media constraints and publication approval requirements still apply. Current image limits are Instagram 10, TikTok 35, Facebook 10, LinkedIn 20, Threads 20, X 4 and Bluesky 4; the app rejects oversized carousels instead of dropping slides.
Direct TikTok submission through generic MCP publish_post or schedule_post is intentionally rejected. The user must view that post in the Caroush composer and complete creator-specific privacy, media-preview and consent controls. Automation definitions may target TikTok, but the existing pipeline holds the completed TikTok output for per-post review.
Generation automations support:
| Kind | Existing templates | Trigger | Output |
|---|---|---|---|
| Video | 2×2 grid; single fade | Daily HH:mm times in an IANA timezone; manual run | Generated portrait video and publication destinations |
| Video/UGC | Presenter clip, written overlay, required CTA video, optional music | Daily times; manual run | Merged portrait UGC video and publication destinations |
| Carousel | Illustrated comparison; PIE editorial; relatable POV | Daily times; manual run | Ordered carousel slides and publication destinations |
New MCP definitions are drafts. Enabling and running are separate approval-required actions. Prices come from existing runtime credit configuration: at current defaults, generated images are 5 credits each, 2×2 is 20 credits, single fade is 5 credits, and UGC automation is 10 credits. Uploaded carousel CTA artwork replaces one generated slide and reduces the generated-image charge. The credit ledger performs reservation, capture and refunds; clients cannot submit a price.
Legacy posting folders support weekly slots for existing approved drafts. They are distinct from the daily generation automation tool family. No first-class campaign model, arbitrary trigger graph, RSS/event-triggered generation or optimal posting-time predictor was found; do not describe these as available MCP tools. A folder named “Campaign” is not a campaign API.
Analytics semantics
MCP analytics read saved real snapshots and observations for posts actually delivered through Caroush. Reads do not fetch an account feed or contact providers. The existing Pro analytics entitlement applies.
The daily sync runs at 00:00 America/New_York by default. This timezone changes with New York daylight-saving time. The configured application scheduler dispatches individual jobs to the existing database queue. Each scheduled daily sync has a unique post/source/date ledger entry.
Reported metrics depend on platform and granted permissions:
| Platform | Existing supported post metrics, when returned by the provider |
|---|---|
| Likes, comments, views, reach, saves, shares, reposts | |
| Reactions/likes, comments, shares, eligible Page/video views and reach | |
| Reactions, comments, reshares, impressions, reach, clicks, eligible video views; newer member API saves/sends where available | |
| TikTok | Confirmed video views, likes, comments, shares; photo/inbox metrics may be unavailable |
| X/Twitter | Likes, replies, reposts, quotes, bookmarks and impressions; shared-media video views are deliberately excluded |
| Threads | Views, likes, replies, shares, reposts, quotes |
| Bluesky | Likes, replies, reposts, quotes |
A missing metric stays unavailable, not zero. Publication-date filtering selects a cohort of posts; headline metrics are lifetime totals at their last sync. activityTrend reports observed changes between consecutive stored observations and exposes coverage. Summing post reach does not produce unique people across posts. get_best_performing_content ranks up to five posts only where a views metric exists, preserving platform differences.
Persistence and operational boundaries
Two additive migrations create the required Passport/workspace authorization tables and MCP operation/audit tables. Application content, credit and publishing tables remain authoritative. Operation arguments and results use encrypted casts; preserve the existing APP_KEY. Browser consent nonces are hashed. Access/refresh-token persistence follows Passport/League formats; clients never receive database or platform secrets.
The SDK endpoint uses normal PHP-FPM. No additional MCP daemon, sidecar or container is required. MCP background work uses the database connection and caroush queue with the existing 1800-second worker. Video/carousel native runs then execute on their existing dedicated queues.
Audit events record user/workspace/grant/request/operation identifiers, tool name, result status, timing, error code and argument names. They do not record prompts, captions, uploaded contents, tokens or raw provider errors. Existing error reporting routes unexpected failures to Sentry. This does not add a second product-analytics SDK.
Implementation file inventory
Paths below are relative to the Laravel repository. The complete tool inventory and exact schemas are generated into catalog.json and tools.md in the documentation download.
Created
| Paths | Purpose |
|---|---|
app/Mcp/CaroushServer.php, CaroushTool.php, ToolCatalog.php, SchemaValidator.php, ToolExecutor.php | Protocol adapter, catalog, validation and invocation |
app/Mcp/McpContext.php, McpFailure.php, Audit.php, OperationService.php, ApprovalReview.php, Models/McpOperation.php | Scoped context, safe failures, audit, durable operations and review projections |
app/Mcp/Domains/CoreTools.php, ContentTools.php, OperationsTools.php | Account, content and social/automation tool definitions |
app/Mcp/Auth/ | OAuth configuration/provider, authorization service/model, authentication, token/code/scope repositories and encrypted token response |
app/Mcp/Middleware/ | Enable/host/Origin checks, CORS, context binding, scopes and preserved OAuth challenges |
app/Http/Controllers/Mcp/DocumentationController.php, IntegrationController.php, OAuthController.php | Documentation, connections, browser approvals and OAuth endpoints |
app/Providers/McpServiceProvider.php, routes/mcp.php | Registration, routes and scheduled maintenance |
app/Jobs/ExecuteMcpOperation.php, app/Console/Commands/McpMaintain.php | Background execution and bounded recovery |
app/Mcp/Documentation.php, app/Console/Commands/ExportMcpDocs.php | Generated website, schemas and portable ZIP export |
app/Services/Caroush/AutomationManagement.php, CarouselContent.php, ContentLibrary.php, PostContent.php, PostScheduling.php, ProductContent.php, SocialOperations.php, StoredMediaUpload.php, UgcHook.php | Shared native application services and owner-scoped projections |
config/caroush_mcp.php, config/passport.php, .env.example | Runtime configuration and safe placeholders |
database/migrations/2026_09_28_210000_create_mcp_oauth_tables.php | OAuth records, workspace grants and consent nonces |
database/migrations/2026_09_28_211000_create_mcp_operations_tables.php | Encrypted operations and audit events |
resources/views/mcp/authorize.blade.php, integration.blade.php, review.blade.php, docs.blade.php, styles.blade.php | Consent, settings, approvals and documentation UI |
tests/Caroush/McpOAuthTest.php, McpProtocolTest.php, McpExecutorTest.php, McpContentToolsTest.php, McpOperationsToolsTest.php, McpDocumentationTest.php, McpApprovalReviewTest.php | Protocol, security, domain integration and UI projection coverage |
docs/mcp.md, docs/mcp-architecture.md, docs/mcp-deployment.md | Developer reference, architecture and deployment handover |
Modified
| Paths | Change |
|---|---|
composer.json, composer.lock | Locked Laravel MCP, Passport and JSON Schema dependencies |
bootstrap/providers.php, config/auth.php, app/Models/User.php | Register MCP/OAuth and support Passport without replacing browser authentication |
app/Services/Caroush/Account.php | Pin request-local authorized workspace context |
app/Http/Controllers/Caroush/CarouselController.php, PostController.php, ProductController.php, UgcVideoController.php | Delegate existing behavior to shared services |
app/Services/Caroush/CarouselAutomation/AutomationController.php, app/Services/Caroush/VideoAutomation/AutomationController.php | Share lifecycle handling with MCP |
app/Support/SentrySanitizer.php, tests/Caroush/SentrySanitizerTest.php | Filter MCP/OAuth/queued-operation payloads from error telemetry |
resources/js/app/(app)/settings/settings-client.tsx | Link to AI connections |
Dockerfile.coolify, Dockerfile.coolify.dockerignore | Include maintained documentation in the runtime image |
.gitignore | Permit the safe environment example while keeping private configuration excluded |
public/build/manifest.json, public/build/assets/* | Rebuilt frontend assets for the settings change |
Private .env and .env.production files were not changed. The populated application database and production configuration were not migrated or modified during implementation; automated migration checks ran against isolated test databases.