A remote Model Context Protocol server. It exposes the MalGomo agency dashboard
(revenue KPIs, fan growth, earnings) as tools for an AI agent. It is a thin, read-only wrapper over the
backend REST at https://api.malgomo.com — all authentication, agency scoping and the owner/admin gate
live in the backend; this server only carries your token.
| Method & path | Purpose |
|---|---|
POST /mcp | The MCP endpoint (JSON-RPC over Streamable HTTP). Connect your client here. |
GET /mcp | Server-to-client event stream for an open session (used by the MCP client). |
DELETE /mcp | Ends an MCP session. |
GET /healthz | Liveness probe — returns {"status":"ok"}. |
GET / · GET /docs | This 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.
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:
POST /mcp (it performs the MCP initialize handshake and gets a session id).malgomo_login tool with your MalGomo email and password.https://xgifdbzqplddpeobtwtw.supabase.co) for a short-lived token, stores it
for this session only, and probes whether your account may read dashboard data.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.
Log in with email + password. Verifies once per session.
| Param | Type | Notes |
|---|---|---|
email | string | Your MalGomo CRM email. |
password | string | Your CRM password. Used once, never stored. |
Reports whether the connection is logged in, as whom, and the token expiry. No arguments.
Clears the session token from this connection. No arguments.
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.
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.
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.
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.
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).
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.
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.
| Param | Type | Notes |
|---|---|---|
profile_name | string | Required. The creator the broadcast is for. |
script_id | number | Required. The saved script to load (see malgomo_get_scripts). |
sendToLists | string[] | Required, ≥1. Recipient lists (Senden an). |
excludeLists | string[]? | Lists to exclude (Nicht senden an). |
schedule | object? | {mode:"now"} or {mode:"scheduled", dayLabel, hour, minute}. |
Returns the resolved ComposePlan.
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.
| Param | Type | Notes |
|---|---|---|
profile_name | string | Required. |
script_id | number? | A saved script to load. Provide this OR message. |
message | object? | A literal message {message, vaultMediaIds?, priceCents?}. Provide this OR script_id. |
sendToLists | string[] | Required, ≥1. |
excludeLists | string[]? | Lists to exclude. |
schedule | object | Required. {mode:"now"} or {mode:"scheduled", dayLabel, hour, minute}. |
dry_run | boolean | Default true. false creates the awaiting-approval draft. |
expires_in_hours | number? | 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.
Lists broadcast intents and their lifecycle (status, plan, result). Owner/admin only. Optional filters: profile_name, status.
Returns the verbatim ComposeSendResult for one intent once executed (and its status/plan before then). Owner/admin only. Param: intent_id (string, required).
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?}.
| Value | Window |
|---|---|
today (default) | Current day |
yesterday | Previous day |
7d | Fans & earnings: last 7 days. KPIs: Maloum's current week. |
30d | Fans & 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.
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.
forbidden.awaiting_approval DRAFT — they can NEVER fire a real mass send, which always requires a human to approve in the desktop app.mcp-session-id is a connection secret (it rides your logged-in token). It is a random UUID sent only over TLS — treat it like a bearer token; do not share it.