MalGomo MCP Server v1.6.0

A remote Model Context Protocol server. It exposes the MalGomo agency dashboard (revenue KPIs, fan growth, earnings), broadcast drafting, feedback tickets and the Media Studio (AI image/video generation) as tools for an AI agent. It is a thin wrapper over the backend REST at https://api.malgomo.com — all authentication, agency scoping and the permission gates (owner/admin for the dashboard tools, the per-member is_dev grant for the ticket tools, the per-member media_studio_access grant for the Media Studio tools) live in the backend; this server only carries your token. This server is NOT purely read-only: the two generation tools (malgomo_generate_image, malgomo_generate_video, malgomo_generate_voice) immediately spend real money from the logged-in user's media budget — see section 5.

1. Endpoints

Method & pathPurpose
POST /mcpThe MCP endpoint (JSON-RPC over Streamable HTTP). Connect your client here.
GET /mcpServer-to-client event stream for an open session (used by the MCP client).
DELETE /mcpEnds an MCP session.
GET /healthzLiveness probe — returns {"status":"ok","service":…,"version":…}. The version tells a landed deploy from a queued one.
GET / · GET /docsThis documentation page.

When OAuth is enabled (PUBLIC_URL set), the server also serves the standard OAuth endpoints — /authorize (sign-in page), /oauth/login, /token, /register (Dynamic Client Registration), /revoke — plus discovery metadata at /.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource/mcp. Your MCP client uses these automatically; you do not call them by hand.

2. Logging in

Authentication uses your normal MalGomo CRM email + password. There are two modes:

A) OAuth at connect (recommended, when the server has PUBLIC_URL set). When you add the connector, the client discovers the login and opens a sign-in page in your browser. You log in once; the client stores the token and refreshes it automatically — no per-chat login. The /mcp endpoint requires a valid bearer token in this mode.

B) In-chat tool (when OAuth is off). Session-based — log in once per connection via the malgomo_login tool, then every data tool works without re-auth:

  1. Connect your MCP client to POST /mcp (it performs the MCP initialize handshake and gets a session id).
  2. Call the malgomo_login tool with your MalGomo email and password.
  3. The server exchanges them with Supabase (https://xgifdbzqplddpeobtwtw.supabase.co) for a short-lived token, stores it for this session only, and probes whether your account may read dashboard data.
  4. Call malgomo_get_kpis / malgomo_get_fans / malgomo_get_earnings / malgomo_get_broadcasts.

The token auto-refreshes on expiry while the connection is open. Credentials are used once and never stored — only the resulting token lives in memory for the session. Disconnecting (or malgomo_logout) clears it.

Access level: dashboard data is owner/admin only. A member account can log in but will get forbidden on the data tools. The Media Studio tools are different: they also work for members that carry the media_studio_access grant (enforced server-side).

3. Tools

malgomo_login (write — auth)

Log in with email + password. Verifies once per session.

ParamTypeNotes
emailstringYour MalGomo CRM email.
passwordstringYour CRM password. Used once, never stored.

malgomo_session_status (read)

Reports whether the connection is logged in, as whom, and the token expiry. No arguments.

malgomo_logout (write)

Clears the session token from this connection. No arguments.

malgomo_version (read, no login)

Which build is serving: {mcp: {name, version}, backend: {service, build_sha, started_at} | {error}}. Use it to tell a landed deploy from a queued one, and include it in any bug report. The backend half is read from its public /version endpoint with a 5 s timeout — an unreachable backend answers {error} rather than failing the call. No arguments.

malgomo_get_kpis (read)

Agency-wide net payout revenue for the chosen period, plus agency economics after per-creator cost cuts.

Returns: hero_net_eur, currency, mobile_period, kpi_period, revenue_by_category_eur, hourly_revenue_current, daily_revenue_current, creator_count, total_agency_earnings_eur, margin_percent, per_creator[] {profile_name, total_eur, gross_eur, agency_earnings_eur, cut_percent, breakdown[] {label, percentage, base, amount_eur}, fetched_at}, latest_fetched_at.

Economics are owner/admin only (absent for members). base="gross" = % of gross; base="agency" = % of the remaining agency share after gross-based items. cut_percent is the share of gross removed; the agency keeps 100 - cut_percent. Use the euro figures directly.

malgomo_get_fans (read)

Fan growth across the agency.

Returns: period, totals {free, sub, total}, new_today_total, new_in_period {free, sub, total}, daily_new[] {at, new_free, new_sub}, per_creator[] {profile_name, free, sub, total, new_today}, profile (the resolved scope), generated_at.

malgomo_get_earnings (read)

Earnings from the payout database.

Returns: period, currency, period_totals {gross, net}, month {gross, net, fee}, daily[] {at, gross, net}, transactions[] {kind, title, fan, net, gross, at, profile_name}, per_creator[] {profile_name, gross, net}, generated_at.

malgomo_get_creator_costs (read)

The configured per-creator cost stack (advisory %, creator %, …), independent of revenue. Owner/admin only. The period argument is ignored.

Returns: currency, creators[] {profile_name, cost_stack[] {label, percentage, base, sort_order}, effective_cut_percent}.

base="gross" = % of gross; base="agency" = % of the remaining agency share. effective_cut_percent is the share of gross the stack removes at any revenue; the agency keeps 100 - effective_cut_percent.

malgomo_get_broadcasts (read)

Per-broadcast (mass message / Massennachricht) performance for the agency's creators, scraped into the broadcast database. Owner/admin only.

Returns a JSON list of rows: {fingerprint, profile_name, sent_at_iso, sent_date, date_raw, recipients, views, buyers, caption, media_count, media_type, price_cents, recalled, revenue_cents, send_to_lists, exclude_from_lists, recipients_scraped_at}. revenue_cents is the derived revenue (price×buyers); recalled flags a recalled broadcast; send_to_lists / exclude_from_lists are the recipient lists (Senden an / Nicht senden an).

malgomo_get_scripts (read)

The agency's reusable chat scripts (message templates), the same ones usable as Massennachrichten. Owner/admin only. The period argument is ignored.

Returns: categories[] {scripts[] {title, message_text, ppv_price, vault_media_id, vault_media_folder_name, creator_profiles, tags}}, a flat tags[], and total_scripts.

Placeholders in message_text (e.g. {fan_name}) are left visible and are NOT auto-substituted — a script with an unresolved placeholder must be resolved to literal text before it can be loaded as a broadcast.

malgomo_preview_script_as_broadcast (read — dry-run)

Maps a saved script to a broadcast plan and shows exactly what would be sent, without queuing anything. Owner/admin only. Rejects a script whose message_text still has an unresolved placeholder.

ParamTypeNotes
profile_namestringRequired. The creator the broadcast is for.
script_idnumberRequired. The saved script to load (see malgomo_get_scripts).
sendToListsstring[]Required, ≥1. Recipient lists (Senden an).
excludeListsstring[]?Lists to exclude (Nicht senden an).
scheduleobject?{mode:"now"} or {mode:"scheduled", dayLabel, hour, minute}.

Returns the resolved ComposePlan.

malgomo_schedule_broadcast (write — queues a DRAFT, never sends)

This does NOT send. An AI agent can draft but never fire a mass send. With dry_run=true (the default) it only returns the resolved plan and queues nothing; with dry_run=false it creates an intent with status="awaiting_approval" that a human must approve in the desktop app before it can fire. Owner/admin only.

ParamTypeNotes
profile_namestringRequired.
script_idnumber?A saved script to load. Provide this OR message.
messageobject?A literal message {message, vaultMediaIds?, priceCents?}. Provide this OR script_id.
sendToListsstring[]Required, ≥1.
excludeListsstring[]?Lists to exclude.
scheduleobjectRequired. {mode:"now"} or {mode:"scheduled", dayLabel, hour, minute}.
dry_runbooleanDefault true. false creates the awaiting-approval draft.
expires_in_hoursnumber?Auto-expire the draft if not approved in time.

Returns {intent_id, status, request_id} when queued, or {dry_run:true, plan} on a dry run.

malgomo_get_scheduled_broadcasts (read)

Lists broadcast intents and their lifecycle (status, plan, result). Owner/admin only. Optional filters: profile_name, status.

malgomo_get_broadcast_intent_result (read)

Returns the verbatim ComposeSendResult for one intent once executed (and its status/plan before then). Owner/admin only. Param: intent_id (string, required).

malgomo_cancel_scheduled_broadcast (write — safe / reversible)

Cancels a queued intent, but only if it has not yet been claimed/executed (nothing was sent). Owner/admin only. Param: intent_id (string, required). Returns {ok, reason?}.

malgomo_get_tickets (read)

Lists feedback/bug tickets submitted from the MalGomo desktop app. Dev only (per-member is_dev grant), across all agencies.

ParamTypeNotes
statusstring?open (default), in_progress, resolved, closed, or all.
limitnumber?Maximum tickets to return.

Returns {tickets[] {id, agency_id, user_id, user_email, user_role, category, description, has_logs, logs_preview, image_count, video_count, app_version, platform, metadata, status, created_at, updated_at}}. Categories: bug | idea | question | other. List rows carry only a logs preview and the media counts — use malgomo_get_ticket for the full logs and the image/video URLs.

malgomo_get_ticket (read)

Returns one ticket in full, including the complete logs field, image_urls (signed URLs, valid ~7 days, for attached screenshots) and video_urls (signed URLs for attached screen recordings), directly fetchable without extra auth. Dev only. Param: ticket_id (string, required). 404 ticket_not_found for an unknown id.

malgomo_update_ticket (write — safe / reversible)

Sets a ticket's status. Nothing is deleted and any status can be set again later. Dev only. Params: ticket_id (string, required), status (open | in_progress | resolved | closed, required). Returns the updated ticket.

3b. Media Studio tools

These tools spend real money. malgomo_generate_image and malgomo_generate_video / malgomo_generate_voice start a paid generation the moment the backend accepts them — no approval step, no cancel. Protection is server-side: the per-user media budget, the model access list and the client_token submit dedup. Available to owner/admin and to members with the media_studio_access grant. All responses have the backend's context block (user/agency ids) stripped, and inline base64 image outputs are replaced by a placeholder — view those in the app gallery.

malgomo_media_models (read)

Lists the available generation models, compacted: {providers, models[] {id, name, provider, kind, description, aspect_ratios?, durations_s?, resolutions?, max_images?, supports, price_cents?}}. price_cents is the estimated cost per generation in cents (video: per base duration, scaled with the requested seconds). Param: kind (image | video, optional filter).

malgomo_media_budget (read)

The logged-in user's Media Studio budget for the current month (spent / remaining cents). budget: null means unlimited (no budget row configured). No arguments.

malgomo_media_voices (read)

The voices malgomo_generate_voice accepts: {enabled, voices[] {voice_id, name, provider, scope, source?, creator_profile?, language?}, assigned}. scope is agency for this agency's own voices and stock for provider voices it may assign from; source is the server's own label (stock | cloned | designed | imported). assigned maps each creator_profile to the voice that speaks for it — pass that profile rather than a raw id. enabled:false means voice is switched off agency-wide and every generation will fail. Call this BEFORE generating: an id outside the list is rejected with voice_not_allowed. Members need Media Studio access; assigning and cloning stay admin-only. No arguments.

malgomo_generate_image (write — PAID, no cancel)

Starts a paid image generation immediately. Do NOT retry automatically — on an unclear outcome check malgomo_media_generations first, and resend the SAME client_token to recover that attempt's job — one token maps to exactly one attempt (a failed attempt counts too); a deliberate new attempt needs a NEW client_token. OpenRouter models return exactly 1 image per job; reference images are capped server-side to the model's maximum; @slug mentions in the prompt pull in packs.

ParamTypeNotes
model_idstringRequired. From malgomo_media_models (kind image).
promptstringRequired, ≤4000 chars. @slug pulls in a pack.
aspect_ratiostring?Must be in the model's list.
resolutionstring?e.g. 1K, 2K.
image_sizestring?Provider-specific size token (fal).
num_imagesnumber?1–4; OpenRouter always yields 1.
reference_image_urlsstring[]?≤16 public URLs (see the upload tool). Capped per model.
client_tokenstring?Idempotency token (1–64 chars, alnum/-/_).

Returns the job row. OpenRouter images run asynchronously — track them with malgomo_media_wait.

malgomo_generate_video (write — PAID, no cancel)

Starts a paid video generation immediately. Same retry rules as the image tool. Without duration_s the server uses the model's smallest duration; a duration outside the model's list is rejected with unsupported_duration.

ParamTypeNotes
model_idstringRequired. From malgomo_media_models (kind video).
promptstringRequired, ≤4000 chars.
duration_snumber?Seconds (1–30); must be in the model's durations_s.
aspect_ratiostring?e.g. 16:9, 9:16.
resolutionstring?Must be in the model's resolutions, e.g. 720p, 1080p, 2K (Hailuo 3).
generate_audioboolean?Native audio track (supported models).
start_image_urlstring?First frame (image-to-video).
end_image_urlstring?Last frame — only together with start_image_url.
reference_image_urlsstring[]?≤7 public URLs. Capped per model.
client_tokenstring?Idempotency token (1–64 chars, alnum/-/_).

malgomo_generate_voice (write — PAID, no cancel)

Starts a paid text-to-speech voice note (Cartesia, kind audio_tts). Same retry rules as the image tool. Priced per 1000 characters; text is capped at 1500. Needs the agency's voice feature enabled (voice_not_enabled otherwise). The finished MP3 is output_urls[0].

ParamTypeNotes
textstringRequired, ≤1500 chars. Inline emotion tags like [flirtatious] or [calm] set the emotion (first tag wins).
creator_profilestring?Speak with this creator's assigned voice.
voice_idstring?Explicit voice id when no creator voice applies.
model_idstring?Defaults to cartesia/sonic-3.6.
client_tokenstring?Idempotency token (1–64 chars, alnum/-/_).

malgomo_media_job (read)

Returns one generation job by id and triggers a server-side provider status refresh. Inline data: outputs are replaced by a placeholder plus inline_media_count. Param: job_id (string, required).

malgomo_media_wait (read — polls)

Polls one job until terminal (completed | failed | cancelled) or the timeout elapses. On timeout it returns the current job state plus a hint — the job keeps running server-side (sweeper); just call the tool again. Params: job_id (string, required), timeout_seconds (number?, 5–80, default 55 — hard-capped because the server sits behind a ~100 s edge).

malgomo_media_generations (read)

Lists the user's generation jobs, newest first, slim (inline outputs blanked). Keyset pagination via before = the previous response's next_before. Params: limit (number?, 1–200), before (string?), favorites_only (boolean?), folder_id (string?).

malgomo_media_favorite (write — safe / reversible)

Marks/unmarks a job as favorite. Params: job_id (string, required), favorited (boolean, required).

malgomo_media_packs (read)

Lists character/element packs. A pack is used by mentioning its @slug in a generate prompt — the server merges its reference images + prompt hint automatically. Param: kind (character | element, optional filter).

malgomo_media_pack (read)

Returns one pack in full. Param: pack_id (string, required).

malgomo_media_download (write — publishes one output)

Returns a plain HTTPS URL for one output of a finished job — fetchable with a simple GET, no auth, valid ~72 h. Inline data: image outputs are published to the public ephemeral host on the first call (and cached); already-public video URLs are returned directly. Params: job_id (string, required), index (number, default 0). This is the way to actually save a generated file from an agent: call it, then GET the returned url.

malgomo_media_upload_reference (write — file goes to a PUBLIC host)

Uploads a base64 file to use as a generation reference and returns its URL for reference_image_urls / start_image_url. The file lands on an external public ephemeral host (Litterbox, publicly fetchable for ~72 h; fallback: MalGomo's own media-refs store, ~24 h) — do not upload anything that must not sit on a public host. Max 16 MB binary.

ParamTypeNotes
kindstringRequired. image | video | audio.
mime_typestringRequired, e.g. image/png.
data_b64stringRequired. Base64 content, ≤16 MB decoded.
filenamestring?Original filename.

period argument (kpis, fans, earnings)

ValueWindow
today (default)Current day
yesterdayPrevious day
7dFans & earnings: last 7 days. KPIs: Maloum's current week.
30dFans & earnings: last 30 days. KPIs: Maloum's current month.

Earnings & fan totals for a period are summed over that rolling day window; KPI revenue maps the token onto Maloum's own named period instead.

4. Connecting a client

Add this server as a remote/custom MCP connector pointed at POST /mcp. With OAuth on (mode A), the client shows a sign-in page at connect time — log in there and you are done. With OAuth off (mode B), no header is required and you authenticate with the malgomo_login tool. (Advanced: send Authorization: Bearer <supabase-jwt> on the connection to skip the login tool.)

Quick raw check with curl (initialize handshake):

curl -i -X POST <this-url>/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
       "params":{"protocolVersion":"2025-06-18","capabilities":{},
       "clientInfo":{"name":"curl","version":"0"}}}'

The response carries an mcp-session-id header — send it back on later requests.

5. Security & scoping