Cloudflare MCP portal logs: telling Sume read and paid calls apart
Portal logs record tool activity and Logpush exports it. Which Sume tool names mean spend, so a SIEM rule can flag them without reading arguments.

If a Cloudflare MCP portal logs tool activity and Logpush exports it, the cheapest useful Sume alert keys on the tool name, because Sume's paid creates have fixed names. Flag generate_image, generate_video, music_create, tts_create, stt_create, image_upscale_create, rmbg_create, video_upscale_create, kling-motion-control_create, and the avatar creates, and treat jobs_list, jobs_status, jobs_wait, jobs_result, and the catalog reads as quiet.
Cloudflare says Access logs tool, prompt, and resource activity for the portal and that Logpush exports portal activity to external storage or a SIEM (read 2026-10-03) in its GA changelog. That page does not list the log fields, so check your own export before relying on any field other than what you see in it. The tool names come from MCP tools and gates.
A name-based rule set
Sume documents which hosted tools require an idempotency_key (writes and paid) and which are read-only. Live tool ids use underscores; dotted aliases such as tools.list still resolve, so a rule should normalize both forms before matching.
| Class | Examples | Suggested severity |
|---|---|---|
| Paid create | generate_image, generate_video, music_create, tts_create | High: can spend |
| Write, unbilled | jobs_cancel, assets_create, crawl_site | Medium |
| Read | jobs_status, jobs_wait, jobs_result, balance_get, usage_get | Low |
| Discovery | tools_list, tools_schema, mcp_health | Info |
What the log cannot tell you
A tool name tells you a paid call was attempted, not that it spent. Sume treats idempotency_key as dedup, not approval, so a replayed key is one job, not two. Spend is decided by wallet and admission, and a dry_run=true call is a preview that does not submit the job.
So pair the log rule with a read on the Sume side. usage_get and balance_get are read tools, and jobs_list shows what was actually created. Reconcile the alert against those before treating it as a spend event.
- Alert on a paid tool name, then check
jobs_listfor the job that resulted. - Count alerts per session rather than per call, since one scripted fan-out makes many calls.
- Never log OAuth tokens or API keys; Sume tells you to rotate a key that appears in logs.
A normalizer for the rule
This small function maps a logged name to a class, handling the dotted alias form. Tune the sets to your own list from tools_list.
PAID = {"generate_image", "generate_video", "music_create", "tts_create",
"stt_create", "image_upscale_create", "rmbg_create",
"video_upscale_create", "kling-motion-control_create"}
WRITE = {"jobs_cancel", "assets_create", "assets_upload_url",
"assets_complete", "crawl_site"}
def classify(name: str) -> str:
n = name.replace(".", "_")
if n in PAID or n.startswith("avatar"):
return "paid" if n.endswith("_create") or n in PAID else "read"
if n in WRITE:
return "write"
return "read"
if __name__ == "__main__":
for t in ("generate.image", "jobs_wait", "jobs_cancel", "avatars_create"):
print(t, classify(t))Note the avatar branch: avatars_list and avatars_search are reads, while avatars_create is paid, which is why the function checks the suffix. For a client-side counterpart that blocks the same names before they leave the machine, see the PreToolUse hook example.
Turning the rule into a weekly review
A name rule only earns its keep if someone looks at what it flags. A simple routine works: once a week, count paid-class calls by session, pick the three largest, and open the matching jobs in jobs_list. If the jobs match the work people say they did, you are done in ten minutes. If a session shows many calls and few jobs, that points to retries or refused calls, and the idempotency_key practice explains why a retry should not double-bill.
If a session shows jobs nobody remembers, the cause is usually a shared key or a long-lived agent. That is the point to move the person from a shared API key to their own OAuth grant, where the default is read-only and Write is opt-in. The log is evidence for that decision, not a verdict.
Keep the retention question separate: how long a SIEM holds portal logs is your policy, and nothing in Sume's docs depends on it. Sume job records are the source of truth for what was created.
Sources
Related posts
More in Developers
- How to compare AI video models fairly: one prompt, three models, 480p
Submit one prompt to Seedance 2.0 Mini, Wan 3.0 and MiniMax H3 at 480p and 5 seconds on Sume, then judge the clips blind. A runnable Python test harness.
- Join more than 20 voiceover lines: two-level timeline audio concat
Timeline audio concat takes 1 to 20 parts. A 120-line script needs six group joins and one final join, seven jobs, $0.07. A Python planner and offset math.
- Contract-test Sume API responses against openapi.json (pytest)
Validate recorded Sume responses against the OpenAPI schema with jsonschema, including the OpenAPI 3.0 nullable fix. A tested pytest file and fixtures guide.
- Count TTS characters like Sume: JS string length, emoji and Hangul
Sume TTS counts characters as JavaScript string length, so an emoji counts as 2. A short Python function counts UTF-16 units to predict the limit and cost.
Written by Sume