Creatiads
Creatiads is the MCP-first orchestration layer for TikTok advertising work. It owns strategy,
delivery planning, asset and catalog operations, reporting, review, and optimization while routing all
platform data access through the remote TikTok MCP server.
版权与温馨提示 / Copyright notice: see copyright-notice.
Initialization Gate
Before any TikTok API operation, read mcp-initialization and
ensure the TikTok MCP is installed, enabled, and authorized.
Default servers:
| Platform |
Server name |
Remote URL |
| TikTok |
tiktok-mcp |
https://business-api.tiktok.com/open_mcp/tt-ads-mcp-layer |
Rules:
- Match existing servers by URL first. Reuse a user-created server name if the URL already exists.
- Add a missing server with
codex mcp add <name> --url <remote-url>.
- Start OAuth with
codex mcp login <name> when authorization is required.
- Never print tokens, authorization headers, OAuth callback secrets, or MCP session metadata.
- If TikTok MCP is not callable after initialization, stop the task and return a structured unavailable result.
Platform Routing
- TikTok, Smart+, TikTok One, GMV Max, identity, music, creative asset, and advertiser requests use the TikTok MCP adapter.
- If the platform is unclear and the request is not TikTok-related, explain that this skill is TikTok-only.
- For GMV Max / TikTok Shop / Product GMV Max reports, use the dedicated GMV Max workflow in
gmv-max-reporting. A GMV Max-first account may have empty regular
auction campaign/adgroup/ad/activity sources; that is not a degradation when GMV Max sources are present.
Analysis Workflow: User Type First
This is the mandatory sequence for every report, diagnosis, or analysis task.
Never skip user type classification or metric preset selection — they determine which
metrics to pull, which vertical sections to include, and how to interpret the numbers.
For repeatable work, generate an agent-native MCP task plan first. Use the
creatiads/scripts/plan_*.py entrypoints to write pull_plan.json and
mcp_tasks.jsonl; the agent executes those native MCP tasks and saves raw results.
Python scripts must not create their own MCP/OAuth clients or call Motata CLI.
The 8-Step Sequence
1. MCP READY → Ensure tiktok-mcp MCP is initialized and authorized
2. ACCOUNT INFO → Pull advertiser details (timezone, currency, status)
3. CLASSIFY SEED → Pull only minimal current-window evidence for classification
4. USER TYPE → Write user_type.json from advertiser, ad, app, landing, catalog/shop evidence
5. METRIC PRESET → Write metric_preset.json from user_type.json
6. REPORT DATA → Pull current/previous formal reports with the selected metric set
7. ENRICHMENT → Pull audience breakdowns, creative previews, landing paths, activities
8. HTML + AUDIT → Produce analysis, HTML, and audit
Step 3 is not the formal report data pull. It may use a tiny current-window seed
(classification_campaigns, classification_adgroups, classification_ads,
classification_ad_v2_insights, app/catalog/shop/Smart+ evidence) only to classify the
advertiser. Formal current_*, previous_*, audience, creative, activity, and metric probe
sources must wait until user_type.json and metric_preset.json exist.
Formal MCP report sources must be complete candidate pools. Do not write top-row
samples as current_campaigns, current_adgroups, current_ads, current_ad_v2_insights,
or previous-period equivalents. Use MCP pagination/page sizes large enough to materialize
every row TikTok reports for that level, and merge all pages before report generation.
If page_info.total_number > row_count or page_info.total_page > 1, the audit must fail
until the missing pages are pulled. Also pull current_advertiser_insights.json and
previous_advertiser_insights.json; these one-row advertiser reports are the KPI totals,
while campaign/adgroup/ad/ad_v2 rows are driver pools.
Step 4 must run before Step 5, and both must run before Step 6. The user type determines
which metrics to pull, which HTML sections to include, what counts as "good" CPA, and what
caveats apply.
Account Vertical Cache
Do not re-classify the same advertiser on every analysis run. When an account-level profile cache
exists and is still fresh, reuse its user_type.json and metric_preset.json instead of pulling
classification seed data again. The default cache TTL is 14 days.
After a fresh vertical classification, list the complete recommended metrics in both Chinese and
English and ask the user whether anything should be added or adjusted. If the user adds metrics,
run one metric probe/data-pull test for those additions first. Write the account profile cache only
after the added metrics are returned as active or supported-empty; otherwise keep the run uncached
and report the failed metric names.
For creative, ad, audience, activity, budget, bottleneck, landing/app, and performance diagnosis,
always include the account vertical context and metric preset. Creative/material and ad analysis must
rank and explain results through the cached or freshly classified vertical metric lens, not just generic
spend/click/conversion totals.
Step 4: User Type Classification
Collect evidence from the current-period data:
- Campaign names, objective types, automation types
- Ad names, ad texts, CTAs
- Landing URLs, app IDs, store URLs
- Advertiser name, industry
- Catalog/shop presence
Classify into one of 13 types using keyword scoring (spend-weighted, domain hints,
app-store detection):
| Type |
Key Signals |
Metric Focus |
ecommerce |
shopify, amazon, product URLs, catalog, shop |
shop metrics, ROAS, add-to-cart |
short_drama |
drama, 短剧, reelshort, 王妃, 重生 |
value/ROAS, IAP, creative retention |
utility_or_w2a |
SaaS, 工具, 批量, 管理, ad management tools |
conversion, app install, CTR |
casual_game |
puzzle, match, casual, hypercasual |
app install, retention, CPI |
midcore_or_hardcore_game |
rpg, strategy, mmo, 传奇, 原神 |
IAP value, ROAS, retention |
novel |
novel, 小说, reader, 阅读 |
value, reading-time, install |
social |
dating, chat, social, live |
messaging, engagement, install |
entertainment |
streaming, media, video, music |
engagement, retention |
financial_leads |
loan, credit, finance, insurance |
lead, form, conversion |
search_arbitrage |
multi-vertical, high-CTR, content farm |
CTR, CPC, impression volume |
earnings_or_offerwall |
earn, reward, cash, offerwall |
install, engagement |
regulated_high_risk |
casino, bet, gambling, 赌 |
value, conversion (limited) |
agency_or_mixed |
3+ types with meaningful scores |
all groups — full probe |
Write user_type.json with top_type, confidence (high/medium/low),
evidence array, and data_gaps.
Step 5: Metric Preset Selection
Map the top user type to metric groups:
ecommerce → [core, conversion, value, shop, creative_video]
short_drama → [core, conversion, value, creative_video, app]
utility_or_w2a → [core, conversion, app, creative_video]
casual_game → [core, conversion, value, app, creative_video]
midcore_or_hardcore_game → [core, conversion, value, app, creative_video]
novel → [core, conversion, value, creative_video, app]
social → [core, conversion, lead, app]
entertainment → [core, conversion, creative_video, app]
financial_leads → [core, conversion, lead]
search_arbitrage → [core, conversion]
earnings_or_offerwall → [core, conversion, lead]
regulated_high_risk → [core, conversion, value]
agency_or_mixed → [core, conversion, value, app, shop, lead, creative_video]
Each group expands to specific TikTok metric names (see metric_probe.py BASE_METRICS).
Write metric_preset.json with {user_type, derived_user_type, groups[], metrics[], source_user_type_hash}. source_user_type_hash must match the current user_type.json so
the audit can prove the preset was selected after classification.
At depth standard, skip the formal metric probe — use the preset to guide which
additional metrics to pull in Step 6 enrichment. At depth full/deep, run the metric
probe to classify each recommended metric as active/supported_empty/unsupported.
Step 6: Enrichment (depth-dependent)
quick is accepted as an alias for fast. Keep the source contract aligned with
Motata's quick/standard/full/deep behavior:
| Source |
quick/fast |
standard |
full |
deep |
| Current advertiser/campaign/ad reports |
yes |
yes |
yes |
yes |
| Current ad group reports |
no |
yes |
yes |
yes |
| Previous advertiser/campaign reports |
yes |
yes |
yes |
yes |
| Previous ad group/ad reports |
no |
yes |
yes |
yes |
Current ad_id_v2 report |
yes |
yes |
yes |
yes |
| Audience country |
yes |
yes |
yes |
yes |
| Audience age/gender |
no |
yes |
yes |
yes |
| Audience placement |
no |
yes |
yes |
yes |
| Audience device/platform |
no |
no |
yes |
yes |
| App summary |
no |
yes |
yes |
yes |
| Landing/app path analysis |
yes |
yes |
yes |
yes |
| Top campaign/adgroup/ad structure |
no |
top 30 |
top 60 |
all pages |
| Smart+ ad details (<=50/batch) |
yes |
yes |
yes |
yes |
| Creative previews (embed ) |
no |
yes |
yes |
yes |
| Creative retention (derived) |
no |
yes |
yes |
yes |
| Targeted activity changelog and factors |
yes |
yes |
yes |
yes |
| Advertiser-wide changelog |
no |
no |
no |
yes |
| Blocking errors / review |
no |
no |
no |
yes |
| Metric probe |
no |
no |
yes |
yes |
Step 8: HTML Generation Rules
The report HTML must include these sections (at minimum):
- Status bar — overall status (normal/watch/urgent) with reason
- KPI snapshot — current vs previous period with deltas
- Campaign leaderboard — ranked by spend, CPA highlighted
- Audience breakdown — country, age×gender, placement, platform
- Creative preview — embedded
<img> tags and <a> links (NOT text labels)
- Activity changelog — key operational events (budget/bid/targeting changes)
- Next actions — P0/P1/P2 priority with specific entity IDs
- Data quality — source status table with row counts and method notes
For the full HTML contract, see report-contract.
Execution Path: L1 Dispatcher First
80%+ of TikTok MCP tools are L1-only — they require tool_execute to reach.
Do NOT assume a tool is unavailable just because it's not listed as a direct MCP function.
Report KPI Exception
report_integrated_get is the canonical KPI and audience report path. For formal
current_*, previous_*, ad_v2, audience, metric probe, and activity-targeted insight
report rows:
- Call the direct MCP report tool only.
- Do not use
tool_execute, tool_get, tool_list, direct HTTP, curl, or Motata data.
- Do not retry a formal KPI source with lean metrics unless the task retry ladder explicitly
reaches that fallback. Record the fallback reason in
attempts.
- If a direct report response is large, recover the full JSON from the subagent MCP event log
(
mcp_tool_call_end) instead of trusting truncated chat transcript text.
Resolution order for every tool call:
- Try L0 direct first (e.g.
mcp__tiktok-mcp__smart_plus_ad_get)
- If "unknown tool" or
structured_unavailable, immediately fall back to L1 dispatcher:
mcp__tiktok-mcp__tool_execute(tool_name="ExactL1ToolName", params={{...}})
- Only if both fail, mark the source as
structured_unavailable
L0 tools (direct call — no dispatcher):
Smart+ CRUD: smart_plus_campaign_get, smart_plus_adgroup_get, smart_plus_ad_get
and their Create/Update/Status variants. Plus: report_integrated_get, advertiser_info_get,
app_list_get, pixel_list_get, page_get, identity_get, file_image_ad_search,
smart_plus_material_report_overview_run, smart_plus_material_report_breakdown_run,
smart_plus_ad_preview.
Critical L1-only tools (require tool_execute):
| L1 Tool |
Purpose |
ad_get, adgroup_get, campaign_get |
Regular (non-Smart+) entity detail |
file_image_ad_info_get, file_video_ad_info_get |
Image/video preview URLs |
tt_video_list_get, tt_video_info_get |
Spark Ad posters and previews |
changelog_task_create → changelog_task_check → changelog_task_download |
Activity changelog |
ad_review_info_get, adgroup_review_info_get |
Blocking errors |
v2 no new_name for media upload |
Media upload currently unavailable in v2 MCP |
| All audience, targeting, BC, GMV Max, creative portfolio tools |
Discovery and management |
For activity changelog, first verify the exact L1 tool names with tool_list/tool_get.
If changelog/activity-log tools are not exposed in the current TikTok MCP server, write
activity_changelog.json as structured_unavailable with every attempted tool and error.
Then derive activities, activity_targeted_insights, activity_daily_breakdown, and
activity_factors from that unavailable source so the report can finish with explicit
degradation instead of hanging.
Creative Preview HTML Rule
When preview URLs are resolved, they MUST be embedded in HTML as real <img> tags and
clickable <a> links. Never output bare text like "inline_image" in place of actual media.
The URL IS the preview. Before marking a report complete, verify at least one <img> tag
appears in the creative preview section.
Task Routing Matrix
When the user asks for analysis, route to the correct workflow by intent:
| User Intent |
Trigger Keywords |
Follow |
| Daily pulse / snapshot |
"日报", "daily pulse", "today", "昨天" |
tiktok-report-runner + 8-step sequence above |
| Weekly report |
"周报", "weekly report", "这周", "上周" |
tiktok-report-runner + 8-step sequence above |
| User type / vertical diagnosis |
"这是什么垂类", "what vertical", "账户类型" |
tiktok-analysis-playbooks § Advertiser Type |
| Metric capability / preset |
"有哪些指标", "metrics probe", "指标探测" |
vertical-metric-playbooks |
| Audience diagnosis |
"受众", "audience", "targeting" |
audience-optimization |
| Creative / video fatigue |
"创意", "素材", "creative fatigue", "疲劳" |
creative-analysis |
| GMV Max / TikTok Shop report |
"GMV Max", "Product GMV Max", "TikTok Shop", "商品周报", "店铺周报" |
gmv-max-reporting |
| Landing page / funnel |
"落地页", "landing page", "W2A", "app path" |
landing-page-and-funnel |
| Budget / bid optimization |
"预算", "出价", "budget", "bid", "scale" |
budget-and-bid-optimization |
| Measurement / attribution |
"归因", "attribution", "SKAN", "measurement" |
measurement-and-attribution |
| Cross-account rebuild |
"迁移", "rebuild", "clone", "复制账户" |
tiktok-validation-and-rebuild |
| Error / degradation handling |
"报错", "error", "permission denied", "degraded" |
error-cache-degradation |
| Formal HTML report |
"出报告", "生成报告", "report", "HTML" |
report-contract + report-validation |
Reference Map
- MCP setup and auth: mcp-initialization
- Tool call rules and adapter contract: mcp-data-plane
- TikTok tool mapping: tiktok-mcp-tool-map
- TikTok tool parameters and enum values: tiktok-tool-parameters
- TikTok interface parity: tiktok-cli-sdk-mcp-parity
- TikTok operation parity map: tiktok-operation-map
- TikTok analysis playbooks: tiktok-analysis-playbooks
- Vertical metric playbooks: vertical-metric-playbooks
- Vertical report templates: vertical-report-templates
- Audience optimization: audience-optimization
- Creative analysis: creative-analysis
- TikTok creative preview resolution: tiktok-creative-preview-resolution
- GMV Max reporting: gmv-max-reporting
- Landing page and funnel: landing-page-and-funnel
- Budget and bid optimization: budget-and-bid-optimization
- Measurement and attribution: measurement-and-attribution
- Error, cache, and degradation policy: error-cache-degradation
- TikTok report runner: tiktok-report-runner
- TikTok validation and rebuild: tiktok-validation-and-rebuild
- Reports and artifacts: report-contract
- Report validation and audit: report-validation
Safety Rules
- Prefer read-first workflows.
- For create, update, activate, delete, share, budget, or status changes, read the target object first, summarize the intended payload and risk, and get explicit user approval before acting.
- Create new campaigns, ad groups, and ads in paused or disabled state by default.
- Activation is a separate approval step and must not be bundled with creation.
- Distinguish
unsupported, supported_empty, permission_denied, rate_limited, partial, and degraded in results.
- Do not use non-MCP data sources unless the user explicitly asks for an external fallback.
Reporting
For substantive analysis or client-facing output, materialize MCP data under:
build/creatiads_runs/<platform>_<account_or_advertiser>_<period>_<depth>_<until>/
Read report-contract before generating reports.
For TikTok reports, also read tiktok-report-runner and follow its
mandatory source order. A matching existing run directory is not evidence that the current task is done:
after skill, mapping, or MCP parity changes, run the MCP pulls again and overwrite or version the run
artifacts with a fresh generated_at. Reuse old artifacts only when the user explicitly asks for cached
inspection.
Before pulling formal report data, writing analysis, or writing HTML, the report run must first produce
user_type.json from current MCP classification evidence for the requested advertiser and date window.
Then write metric_preset.json from that exact user type. Use the preset to choose the formal metric set,
probe plan, vertical sections, caveats, and action logic.
Scripts — Compute-Only (never make MCP calls)
All Python scripts have been refactored to accept MCP data as input and do pure computation.
Scripts never call MCP tools. The agent always makes MCP calls and passes results to scripts.
| Script |
Input |
Output |
Use Case |
run_report.py |
MCP JSON data via --data-dir or stdin |
sources/*.json, manifest.json, report.html |
Full multi-source report assembly |
classify_user_type.py |
Advertiser info, campaign/ad names, URLs, apps |
user_type.json |
13-class vertical classification |
metric_probe.py |
Report rows by metric group |
metric_preset.json, metric_probe.json |
Metric availability check |
creative_enrichment.py |
Ad rows + media info (image/video/Spark URLs) |
creative_previews.json, creative_retention.json |
Derived retention metrics, fatigue ranking |
audience_analysis.py |
Audience report rows (country/age/placement/device) |
audience_breakdowns.json |
Segment tagging (7 tags) |
landing_app_analyzer.py |
Ad URLs, app IDs, Smart+ landing paths |
landing_app_paths.json |
URL canonicalization, spend aggregation |
activity_analysis.py |
Changelog CSV from MCP download |
activities.json, activity_factors.json |
Activity normalization, ranking |
audit_creatiads_report.py |
Run directory path |
report_audit.json |
Report validation including user type/preset/phase order checks |
backend_router.py |
Tasks/phase from pull_plan.json |
backend_routing.json |
Route tasks between native_agent_mcp and mcp_subagent_executor backends |
plan_subagent_execution.py |
mcp_tasks.jsonl |
subagent_execution_plan.json, subagent_tasks/*.jsonl, subagent_prompts/*.md |
Split full/deep MCP tasks into subagent shards |
plan_gmv_max_report.py |
Advertiser ID, date range, optional BC ID |
pull_plan.json, mcp_tasks.jsonl, summary.json |
Product GMV Max / TikTok Shop report plan |
prepare_preview_mcp_calls.py |
Run directory with current_ads and ad detail sources |
Concrete read-only MCP call specs |
Expand final-row preview placeholders for image/video/Spark/catalog enrichment |
recover_subagent_mcp_payloads.py |
Run directory + Codex session JSONL files |
Missing raw/*.json files |
Fast recovery when subagents called MCP but large payloads were not written |
finalize_workflow_run.py |
Run directory + workflow args |
finalize_summary.json, timing_summary.json, final workflow state |
One-command post-MCP recovery, normalization, workflow advance, audit, and timing summary |
bridge_executor.py |
Legacy raw MCP responses via raw/*.json |
Normalized sources/*.json |
Deprecated bridge alias support; do not use for new plans |
normalize_mcp_source.py |
Raw MCP responses |
Normalized source JSON |
Unwrap L0/L1 dispatcher, text blocks, multi-page merge |
validate_pull_state.py |
Run directory with pull_plan.json |
validation_summary.json |
Phase gate, pagination, backend consistency, auth checks |
build_mcp_pull_plan.py |
Advertiser ID, depth, date range |
pull_plan.json, mcp_tasks.jsonl |
Generate agent-native MCP task plan with backend routing |
Dual Backend Architecture
Creatiads uses a dual-backend execution model to separate light classification work from
heavy batch report pulls. The agent makes all MCP calls; backends define how tasks are
organized and validated.
Backends
| Backend |
Scope |
Trigger |
native_agent_mcp |
Account inventory, user type, metric profile, fast/standard reports |
Default for any light task |
mcp_subagent_executor |
Full/deep reports, batch enrichment, activity changelogs, multi-page merges |
Depth full or deep with batch-eligible capability |
bridge_executor |
Deprecated alias |
Legacy fixture/test compatibility only |
Routing Rules
account_inventory, user_type, metric_profile → native_agent_mcp (always)
full_report, performance_diagnosis, audience_diagnosis,
creative_diagnosis, landing_app_paths, activity_changelog,
bottleneck_diagnosis → mcp_subagent_executor (full/deep), else native
Everything else → native_agent_mcp
Subagent-to-native fallback is allowed for single tasks only. Every fallback must be
recorded in both manifest.backend_fallbacks and source.attempts.
Source Contract Extensions
Every source file now carries:
backend: native_agent_mcp or mcp_subagent_executor
auth_status: empty for native/subagent sources; legacy bridge sources may carry auth state
attempts: retry ladder records including backend fallback attempts
MCP Subagent Executor Constraints
- Agent-native only: subagents call the existing TikTok MCP tools; Python never creates an MCP/OAuth client
- Read-only for report flows: no Create/Update/Status/Delete MCP tasks
- Compact return contract: subagents return only status, source paths, row counts, degraded sources, and errors
- Output flow: subagent fetches →
raw/*.json → normalize_mcp_source.py → sources/*.json
- Blocking first: run only
blocking_before_report shards for the first audited report. Shards marked
optional_after_first_report (preview/activity enhancement) must not hold the main report unless the
user explicitly asks for preview repair or enriched rerender.
- Preview placeholders: for
ad_details_for_enrichment and creative_preview_*, run
prepare_preview_mcp_calls.py after dependencies exist and execute the returned concrete MCP calls.
Use item_types, page, and page_size for Spark posts; never use legacy item_type / count.
If an image/video batch partially fails because of permissions, split and keep successful rows.
- Legacy note:
bridge_executor.py remains only as deprecated compatibility glue and is not the new data plane
Subagent Recovery Rules
Real TikTok MCP runs showed that subagents may successfully call MCP but fail to write large
raw payloads to disk, or their final chat transcript may truncate/escape JSON. Treat the MCP
event stream as the source of truth:
- A subagent must write and verify each planned
raw/*.json immediately after the MCP call.
If a raw file cannot be written in that step, stop the shard and recover; do not keep calling
more sources.
- Check
raw/*.json first; never trust a "completed" subagent message without files or
recoverable MCP events.
- If files are missing, inspect the subagent Codex session JSONL and extract the MCP result
from
mcp_tool_call_end.payload.result.Ok.content[0].text.
- Prefer the automated recovery command before manual JSONL inspection:
python3 creatiads/scripts/recover_subagent_mcp_payloads.py --run-dir <run_dir>.
- Prefer
mcp_tool_call_end over function_call_output; the latter can be truncated or
JSON-escaped into an invalid fragment for large report responses.
- Use
subagent_prompts/*.md from plan_subagent_execution.py when spawning workers; the
prompts are pre-scoped to each shard and reduce setup chatter.
- Do not wait more than a short window for a shard that already made MCP calls. Run the
recovery command, rerun
workflow_runner.py, then relaunch only genuinely missing sources.
- The main session may write recovered MCP JSON into
raw/*.json; this still preserves the
subagent execution model because the platform call happened inside the subagent.
- For large sources, use small page sizes or page-range shards only when event recovery is
unavailable. Otherwise one complete direct report call is acceptable if
mcp_tool_call_end
contains the full payload.
- Close subagents after their result has been recovered or recorded.
Backend Consistency Checks
validate_pull_state.py enforces:
- All tasks in the same phase must use the same backend
- Executor auth failures (
auth_required/permission_denied) must not be masked by status: ok
- Manifest fallback entries must have corresponding source attempts
audit_creatiads_report.py checks for MCP session metadata leaks and missing backend fields
1---2id: creatiads3name: creatiads4description: "TikTok MCP-first advertising orchestrator with 8-step analysis workflow. Use when the user asks colloquially to list/查看广告账户或campaign (\"我有哪些广告账户\", \"列出campaign\", \"看看我的广告\", \"有哪些账户\"), find top/worst performers (\"哪个广告效果最好\", \"表现最好的campaign\", \"最差的广告\", \"浪费预算的广告\", \"best/worst performing ads\", \"top campaigns\"), or wants to inspect, plan, launch, diagnose, report, or optimize — including daily pulse/日报/weekly report/周报, vertical/user type classification/垂类诊断, creative fatigue analysis/创意素材分析, audience optimization/受众分析, landing page & W2A path/落地页分析, budget & bid/预算出价优化, measurement & SKAN attribution/归因分析, activity changelog/操作记录, cross-account rebuild/账户迁移, error & review handling/审核报错诊断, or generate HTML reports/出报告/生成报告. Also use for any of 13 verticals: ecommerce, short_drama/短剧, utility_or_w2a/工具, casual_game/休闲游戏, midcore_or_hardcore_game/中重度游戏, novel/小说, social/社交, entertainment/泛娱乐, financial_leads/金融借贷, search_arbitrage/搜索套利, earnings_or_offerwall, regulated_high_risk, agency_or_mixed/代理商. Covers TikTok, Smart+, TikTok One, GMV Max/Product GMV Max/TikTok Shop reports, Pangle, Spark Ads, carousel ads, catalog/shop ads, creative previews, product catalogs, pixels, audiences, and campaign performance. Uses L1 dispatcher for 80%+ tools; scripts are compute-only. Must initialize the remote MCP server before any platform API operation and use MCP tools as the default data plane."5category: TikTok6homepage: https://www.creatiads.com/7metadata:8 {9 "openclaw":10 {11 "emoji": "ads",12 "requires": {13 "mcp": ["tiktok-mcp"]14 }15 }16 }17---1819# Creatiads2021Creatiads is the MCP-first orchestration layer for TikTok advertising work. It owns strategy,22delivery planning, asset and catalog operations, reporting, review, and optimization while routing all23platform data access through the remote TikTok MCP server.2425版权与温馨提示 / Copyright notice: see [copyright-notice](references/copyright-notice.md).2627## Initialization Gate2829Before any TikTok API operation, read [mcp-initialization](references/mcp-initialization.md) and30ensure the TikTok MCP is installed, enabled, and authorized.3132Default servers:3334| Platform | Server name | Remote URL |35| --- | --- | --- |36| TikTok | `tiktok-mcp` | `https://business-api.tiktok.com/open_mcp/tt-ads-mcp-layer` |3738Rules:3940- Match existing servers by URL first. Reuse a user-created server name if the URL already exists.41- Add a missing server with `codex mcp add <name> --url <remote-url>`.42- Start OAuth with `codex mcp login <name>` when authorization is required.43- Never print tokens, authorization headers, OAuth callback secrets, or MCP session metadata.44- If TikTok MCP is not callable after initialization, stop the task and return a structured unavailable result.4546## Platform Routing4748- TikTok, Smart+, TikTok One, GMV Max, identity, music, creative asset, and advertiser requests use the TikTok MCP adapter.49- If the platform is unclear and the request is not TikTok-related, explain that this skill is TikTok-only.50- For GMV Max / TikTok Shop / Product GMV Max reports, use the dedicated GMV Max workflow in51 [gmv-max-reporting](references/gmv-max-reporting.md). A GMV Max-first account may have empty regular52 auction campaign/adgroup/ad/activity sources; that is not a degradation when GMV Max sources are present.5354## Analysis Workflow: User Type First5556**This is the mandatory sequence for every report, diagnosis, or analysis task.**57Never skip user type classification or metric preset selection — they determine which58metrics to pull, which vertical sections to include, and how to interpret the numbers.5960For repeatable work, generate an agent-native MCP task plan first. Use the61`creatiads/scripts/plan_*.py` entrypoints to write `pull_plan.json` and62`mcp_tasks.jsonl`; the agent executes those native MCP tasks and saves raw results.63Python scripts must not create their own MCP/OAuth clients or call Motata CLI.6465### The 8-Step Sequence6667```681. MCP READY → Ensure tiktok-mcp MCP is initialized and authorized692. ACCOUNT INFO → Pull advertiser details (timezone, currency, status)703. CLASSIFY SEED → Pull only minimal current-window evidence for classification714. USER TYPE → Write user_type.json from advertiser, ad, app, landing, catalog/shop evidence725. METRIC PRESET → Write metric_preset.json from user_type.json736. REPORT DATA → Pull current/previous formal reports with the selected metric set747. ENRICHMENT → Pull audience breakdowns, creative previews, landing paths, activities758. HTML + AUDIT → Produce analysis, HTML, and audit76```7778**Step 3 is not the formal report data pull.** It may use a tiny current-window seed79(`classification_campaigns`, `classification_adgroups`, `classification_ads`,80`classification_ad_v2_insights`, app/catalog/shop/Smart+ evidence) only to classify the81advertiser. Formal `current_*`, `previous_*`, audience, creative, activity, and metric probe82sources must wait until `user_type.json` and `metric_preset.json` exist.8384**Formal MCP report sources must be complete candidate pools.** Do not write top-row85samples as `current_campaigns`, `current_adgroups`, `current_ads`, `current_ad_v2_insights`,86or previous-period equivalents. Use MCP pagination/page sizes large enough to materialize87every row TikTok reports for that level, and merge all pages before report generation.88If `page_info.total_number > row_count` or `page_info.total_page > 1`, the audit must fail89until the missing pages are pulled. Also pull `current_advertiser_insights.json` and90`previous_advertiser_insights.json`; these one-row advertiser reports are the KPI totals,91while campaign/adgroup/ad/ad_v2 rows are driver pools.9293**Step 4 must run before Step 5, and both must run before Step 6.** The user type determines94which metrics to pull, which HTML sections to include, what counts as "good" CPA, and what95caveats apply.9697### Account Vertical Cache9899Do not re-classify the same advertiser on every analysis run. When an account-level profile cache100exists and is still fresh, reuse its `user_type.json` and `metric_preset.json` instead of pulling101classification seed data again. The default cache TTL is 14 days.102103After a fresh vertical classification, list the complete recommended metrics in both Chinese and104English and ask the user whether anything should be added or adjusted. If the user adds metrics,105run one metric probe/data-pull test for those additions first. Write the account profile cache only106after the added metrics are returned as active or supported-empty; otherwise keep the run uncached107and report the failed metric names.108109For creative, ad, audience, activity, budget, bottleneck, landing/app, and performance diagnosis,110always include the account vertical context and metric preset. Creative/material and ad analysis must111rank and explain results through the cached or freshly classified vertical metric lens, not just generic112spend/click/conversion totals.113114### Step 4: User Type Classification115116Collect evidence from the current-period data:117- Campaign names, objective types, automation types118- Ad names, ad texts, CTAs119- Landing URLs, app IDs, store URLs120- Advertiser name, industry121- Catalog/shop presence122123Classify into one of 13 types using keyword scoring (spend-weighted, domain hints,124app-store detection):125126| Type | Key Signals | Metric Focus |127|---|---|---|128| `ecommerce` | shopify, amazon, product URLs, catalog, shop | shop metrics, ROAS, add-to-cart |129| `short_drama` | drama, 短剧, reelshort, 王妃, 重生 | value/ROAS, IAP, creative retention |130| `utility_or_w2a` | SaaS, 工具, 批量, 管理, ad management tools | conversion, app install, CTR |131| `casual_game` | puzzle, match, casual, hypercasual | app install, retention, CPI |132| `midcore_or_hardcore_game` | rpg, strategy, mmo, 传奇, 原神 | IAP value, ROAS, retention |133| `novel` | novel, 小说, reader, 阅读 | value, reading-time, install |134| `social` | dating, chat, social, live | messaging, engagement, install |135| `entertainment` | streaming, media, video, music | engagement, retention |136| `financial_leads` | loan, credit, finance, insurance | lead, form, conversion |137| `search_arbitrage` | multi-vertical, high-CTR, content farm | CTR, CPC, impression volume |138| `earnings_or_offerwall` | earn, reward, cash, offerwall | install, engagement |139| `regulated_high_risk` | casino, bet, gambling, 赌 | value, conversion (limited) |140| `agency_or_mixed` | 3+ types with meaningful scores | all groups — full probe |141142Write `user_type.json` with `top_type`, `confidence` (high/medium/low),143`evidence` array, and `data_gaps`.144145### Step 5: Metric Preset Selection146147Map the top user type to metric groups:148149```150ecommerce → [core, conversion, value, shop, creative_video]151short_drama → [core, conversion, value, creative_video, app]152utility_or_w2a → [core, conversion, app, creative_video]153casual_game → [core, conversion, value, app, creative_video]154midcore_or_hardcore_game → [core, conversion, value, app, creative_video]155novel → [core, conversion, value, creative_video, app]156social → [core, conversion, lead, app]157entertainment → [core, conversion, creative_video, app]158financial_leads → [core, conversion, lead]159search_arbitrage → [core, conversion]160earnings_or_offerwall → [core, conversion, lead]161regulated_high_risk → [core, conversion, value]162agency_or_mixed → [core, conversion, value, app, shop, lead, creative_video]163```164165Each group expands to specific TikTok metric names (see `metric_probe.py` BASE_METRICS).166Write `metric_preset.json` with `{user_type, derived_user_type, groups[], metrics[],167source_user_type_hash}`. `source_user_type_hash` must match the current `user_type.json` so168the audit can prove the preset was selected after classification.169170**At depth standard**, skip the formal metric probe — use the preset to guide which171additional metrics to pull in Step 6 enrichment. **At depth full/deep**, run the metric172probe to classify each recommended metric as active/supported_empty/unsupported.173174### Step 6: Enrichment (depth-dependent)175176`quick` is accepted as an alias for `fast`. Keep the source contract aligned with177Motata's quick/standard/full/deep behavior:178179| Source | quick/fast | standard | full | deep |180|---|---|---|---|---|181| Current advertiser/campaign/ad reports | yes | yes | yes | yes |182| Current ad group reports | no | yes | yes | yes |183| Previous advertiser/campaign reports | yes | yes | yes | yes |184| Previous ad group/ad reports | no | yes | yes | yes |185| Current `ad_id_v2` report | yes | yes | yes | yes |186| Audience country | yes | yes | yes | yes |187| Audience age/gender | no | yes | yes | yes |188| Audience placement | no | yes | yes | yes |189| Audience device/platform | no | no | yes | yes |190| App summary | no | yes | yes | yes |191| Landing/app path analysis | yes | yes | yes | yes |192| Top campaign/adgroup/ad structure | no | top 30 | top 60 | all pages |193| Smart+ ad details (<=50/batch) | yes | yes | yes | yes |194| Creative previews (embed <img>) | no | yes | yes | yes |195| Creative retention (derived) | no | yes | yes | yes |196| Targeted activity changelog and factors | yes | yes | yes | yes |197| Advertiser-wide changelog | no | no | no | yes |198| Blocking errors / review | no | no | no | yes |199| Metric probe | no | no | yes | yes |200201### Step 8: HTML Generation Rules202203The report HTML must include these sections (at minimum):2042051. **Status bar** — overall status (normal/watch/urgent) with reason2062. **KPI snapshot** — current vs previous period with deltas2073. **Campaign leaderboard** — ranked by spend, CPA highlighted2084. **Audience breakdown** — country, age×gender, placement, platform2095. **Creative preview** — embedded `<img>` tags and `<a>` links (NOT text labels)2106. **Activity changelog** — key operational events (budget/bid/targeting changes)2117. **Next actions** — P0/P1/P2 priority with specific entity IDs2128. **Data quality** — source status table with row counts and method notes213214For the full HTML contract, see [report-contract](references/report-contract.md).215216## Execution Path: L1 Dispatcher First217218**80%+ of TikTok MCP tools are L1-only** — they require `tool_execute` to reach.219Do NOT assume a tool is unavailable just because it's not listed as a direct MCP function.220221### Report KPI Exception222223`report_integrated_get` is the canonical KPI and audience report path. For formal224`current_*`, `previous_*`, `ad_v2`, audience, metric probe, and activity-targeted insight225report rows:226227- Call the direct MCP report tool only.228- Do not use `tool_execute`, `tool_get`, `tool_list`, direct HTTP, curl, or Motata data.229- Do not retry a formal KPI source with lean metrics unless the task retry ladder explicitly230 reaches that fallback. Record the fallback reason in `attempts`.231- If a direct report response is large, recover the full JSON from the subagent MCP event log232 (`mcp_tool_call_end`) instead of trusting truncated chat transcript text.233234### Resolution order for every tool call:2352361. **Try L0 direct** first (e.g. `mcp__tiktok-mcp__smart_plus_ad_get`)2372. **If "unknown tool" or `structured_unavailable`**, immediately fall back to L1 dispatcher:238 `mcp__tiktok-mcp__tool_execute(tool_name="ExactL1ToolName", params={{...}})`2393. **Only if both fail**, mark the source as `structured_unavailable`240241### L0 tools (direct call — no dispatcher):242243Smart+ CRUD: `smart_plus_campaign_get`, `smart_plus_adgroup_get`, `smart_plus_ad_get`244and their Create/Update/Status variants. Plus: `report_integrated_get`, `advertiser_info_get`,245`app_list_get`, `pixel_list_get`, `page_get`, `identity_get`, `file_image_ad_search`,246`smart_plus_material_report_overview_run`, `smart_plus_material_report_breakdown_run`,247`smart_plus_ad_preview`.248249### Critical L1-only tools (require `tool_execute`):250251| L1 Tool | Purpose |252|---|---|253| `ad_get`, `adgroup_get`, `campaign_get` | Regular (non-Smart+) entity detail |254| `file_image_ad_info_get`, `file_video_ad_info_get` | **Image/video preview URLs** |255| `tt_video_list_get`, `tt_video_info_get` | **Spark Ad posters and previews** |256| `changelog_task_create` → `changelog_task_check` → `changelog_task_download` | Activity changelog |257| `ad_review_info_get`, `adgroup_review_info_get` | Blocking errors |258| v2 no `new_name` for media upload | Media upload currently unavailable in v2 MCP |259| All audience, targeting, BC, GMV Max, creative portfolio tools | Discovery and management |260261For activity changelog, first verify the exact L1 tool names with `tool_list`/`tool_get`.262If changelog/activity-log tools are not exposed in the current TikTok MCP server, write263`activity_changelog.json` as `structured_unavailable` with every attempted tool and error.264Then derive `activities`, `activity_targeted_insights`, `activity_daily_breakdown`, and265`activity_factors` from that unavailable source so the report can finish with explicit266degradation instead of hanging.267268### Creative Preview HTML Rule269270When preview URLs are resolved, they **MUST** be embedded in HTML as real `<img>` tags and271clickable `<a>` links. Never output bare text like "inline_image" in place of actual media.272The URL IS the preview. Before marking a report complete, verify at least one `<img>` tag273appears in the creative preview section.274275## Task Routing Matrix276277When the user asks for analysis, route to the correct workflow by intent:278279| User Intent | Trigger Keywords | Follow |280|---|---|---|281| Daily pulse / snapshot | "日报", "daily pulse", "today", "昨天" | [tiktok-report-runner](references/tiktok-report-runner.md) + 8-step sequence above |282| Weekly report | "周报", "weekly report", "这周", "上周" | [tiktok-report-runner](references/tiktok-report-runner.md) + 8-step sequence above |283| User type / vertical diagnosis | "这是什么垂类", "what vertical", "账户类型" | [tiktok-analysis-playbooks](references/tiktok-analysis-playbooks.md) § Advertiser Type |284| Metric capability / preset | "有哪些指标", "metrics probe", "指标探测" | [vertical-metric-playbooks](references/vertical-metric-playbooks.md) |285| Audience diagnosis | "受众", "audience", "targeting" | [audience-optimization](references/audience-optimization.md) |286| Creative / video fatigue | "创意", "素材", "creative fatigue", "疲劳" | [creative-analysis](references/creative-analysis.md) |287| GMV Max / TikTok Shop report | "GMV Max", "Product GMV Max", "TikTok Shop", "商品周报", "店铺周报" | [gmv-max-reporting](references/gmv-max-reporting.md) |288| Landing page / funnel | "落地页", "landing page", "W2A", "app path" | [landing-page-and-funnel](references/landing-page-and-funnel.md) |289| Budget / bid optimization | "预算", "出价", "budget", "bid", "scale" | [budget-and-bid-optimization](references/budget-and-bid-optimization.md) |290| Measurement / attribution | "归因", "attribution", "SKAN", "measurement" | [measurement-and-attribution](references/measurement-and-attribution.md) |291| Cross-account rebuild | "迁移", "rebuild", "clone", "复制账户" | [tiktok-validation-and-rebuild](references/tiktok-validation-and-rebuild.md) |292| Error / degradation handling | "报错", "error", "permission denied", "degraded" | [error-cache-degradation](references/error-cache-degradation.md) |293| Formal HTML report | "出报告", "生成报告", "report", "HTML" | [report-contract](references/report-contract.md) + [report-validation](references/report-validation.md) |294295## Reference Map296297- MCP setup and auth: [mcp-initialization](references/mcp-initialization.md)298- Tool call rules and adapter contract: [mcp-data-plane](references/mcp-data-plane.md)299- TikTok tool mapping: [tiktok-mcp-tool-map](references/tiktok-mcp-tool-map.md)300- TikTok tool parameters and enum values: [tiktok-tool-parameters](references/tiktok-tool-parameters.md)301- TikTok interface parity: [tiktok-cli-sdk-mcp-parity](references/tiktok-cli-sdk-mcp-parity.md)302- TikTok operation parity map: [tiktok-operation-map](references/tiktok-operation-map.md)303- TikTok analysis playbooks: [tiktok-analysis-playbooks](references/tiktok-analysis-playbooks.md)304- Vertical metric playbooks: [vertical-metric-playbooks](references/vertical-metric-playbooks.md)305- Vertical report templates: [vertical-report-templates](references/vertical-report-templates.md)306- Audience optimization: [audience-optimization](references/audience-optimization.md)307- Creative analysis: [creative-analysis](references/creative-analysis.md)308- TikTok creative preview resolution: [tiktok-creative-preview-resolution](references/tiktok-creative-preview-resolution.md)309- GMV Max reporting: [gmv-max-reporting](references/gmv-max-reporting.md)310- Landing page and funnel: [landing-page-and-funnel](references/landing-page-and-funnel.md)311- Budget and bid optimization: [budget-and-bid-optimization](references/budget-and-bid-optimization.md)312- Measurement and attribution: [measurement-and-attribution](references/measurement-and-attribution.md)313- Error, cache, and degradation policy: [error-cache-degradation](references/error-cache-degradation.md)314- TikTok report runner: [tiktok-report-runner](references/tiktok-report-runner.md)315- TikTok validation and rebuild: [tiktok-validation-and-rebuild](references/tiktok-validation-and-rebuild.md)316- Reports and artifacts: [report-contract](references/report-contract.md)317- Report validation and audit: [report-validation](references/report-validation.md)318319## Safety Rules320321- Prefer read-first workflows.322- For create, update, activate, delete, share, budget, or status changes, read the target object first, summarize the intended payload and risk, and get explicit user approval before acting.323- Create new campaigns, ad groups, and ads in paused or disabled state by default.324- Activation is a separate approval step and must not be bundled with creation.325- Distinguish `unsupported`, `supported_empty`, `permission_denied`, `rate_limited`, `partial`, and `degraded` in results.326- Do not use non-MCP data sources unless the user explicitly asks for an external fallback.327328## Reporting329330For substantive analysis or client-facing output, materialize MCP data under:331332```text333build/creatiads_runs/<platform>_<account_or_advertiser>_<period>_<depth>_<until>/334```335336Read [report-contract](references/report-contract.md) before generating reports.337338For TikTok reports, also read [tiktok-report-runner](references/tiktok-report-runner.md) and follow its339mandatory source order. A matching existing run directory is not evidence that the current task is done:340after skill, mapping, or MCP parity changes, run the MCP pulls again and overwrite or version the run341artifacts with a fresh `generated_at`. Reuse old artifacts only when the user explicitly asks for cached342inspection.343344Before pulling formal report data, writing analysis, or writing HTML, the report run must first produce345`user_type.json` from current MCP classification evidence for the requested advertiser and date window.346Then write `metric_preset.json` from that exact user type. Use the preset to choose the formal metric set,347probe plan, vertical sections, caveats, and action logic.348349### Scripts — Compute-Only (never make MCP calls)350351All Python scripts have been refactored to accept MCP data as input and do pure computation.352**Scripts never call MCP tools.** The agent always makes MCP calls and passes results to scripts.353354| Script | Input | Output | Use Case |355|---|---|---|---|356| `run_report.py` | MCP JSON data via `--data-dir` or stdin | `sources/*.json`, `manifest.json`, `report.html` | Full multi-source report assembly |357| `classify_user_type.py` | Advertiser info, campaign/ad names, URLs, apps | `user_type.json` | 13-class vertical classification |358| `metric_probe.py` | Report rows by metric group | `metric_preset.json`, `metric_probe.json` | Metric availability check |359| `creative_enrichment.py` | Ad rows + media info (image/video/Spark URLs) | `creative_previews.json`, `creative_retention.json` | Derived retention metrics, fatigue ranking |360| `audience_analysis.py` | Audience report rows (country/age/placement/device) | `audience_breakdowns.json` | Segment tagging (7 tags) |361| `landing_app_analyzer.py` | Ad URLs, app IDs, Smart+ landing paths | `landing_app_paths.json` | URL canonicalization, spend aggregation |362| `activity_analysis.py` | Changelog CSV from MCP download | `activities.json`, `activity_factors.json` | Activity normalization, ranking |363| `audit_creatiads_report.py` | Run directory path | `report_audit.json` | Report validation including user type/preset/phase order checks |364| `backend_router.py` | Tasks/phase from `pull_plan.json` | `backend_routing.json` | Route tasks between native_agent_mcp and mcp_subagent_executor backends |365| `plan_subagent_execution.py` | `mcp_tasks.jsonl` | `subagent_execution_plan.json`, `subagent_tasks/*.jsonl`, `subagent_prompts/*.md` | Split full/deep MCP tasks into subagent shards |366| `plan_gmv_max_report.py` | Advertiser ID, date range, optional BC ID | `pull_plan.json`, `mcp_tasks.jsonl`, `summary.json` | Product GMV Max / TikTok Shop report plan |367| `prepare_preview_mcp_calls.py` | Run directory with `current_ads` and ad detail sources | Concrete read-only MCP call specs | Expand final-row preview placeholders for image/video/Spark/catalog enrichment |368| `recover_subagent_mcp_payloads.py` | Run directory + Codex session JSONL files | Missing `raw/*.json` files | Fast recovery when subagents called MCP but large payloads were not written |369| `finalize_workflow_run.py` | Run directory + workflow args | `finalize_summary.json`, `timing_summary.json`, final workflow state | One-command post-MCP recovery, normalization, workflow advance, audit, and timing summary |370| `bridge_executor.py` | Legacy raw MCP responses via `raw/*.json` | Normalized `sources/*.json` | Deprecated bridge alias support; do not use for new plans |371| `normalize_mcp_source.py` | Raw MCP responses | Normalized source JSON | Unwrap L0/L1 dispatcher, text blocks, multi-page merge |372| `validate_pull_state.py` | Run directory with `pull_plan.json` | `validation_summary.json` | Phase gate, pagination, backend consistency, auth checks |373| `build_mcp_pull_plan.py` | Advertiser ID, depth, date range | `pull_plan.json`, `mcp_tasks.jsonl` | Generate agent-native MCP task plan with backend routing |374375## Dual Backend Architecture376377Creatiads uses a dual-backend execution model to separate light classification work from378heavy batch report pulls. The agent makes all MCP calls; backends define *how* tasks are379organized and validated.380381### Backends382383| Backend | Scope | Trigger |384|---|---|---|385| `native_agent_mcp` | Account inventory, user type, metric profile, fast/standard reports | Default for any light task |386| `mcp_subagent_executor` | Full/deep reports, batch enrichment, activity changelogs, multi-page merges | Depth `full` or `deep` with batch-eligible capability |387| `bridge_executor` | Deprecated alias | Legacy fixture/test compatibility only |388389### Routing Rules390391```392account_inventory, user_type, metric_profile → native_agent_mcp (always)393full_report, performance_diagnosis, audience_diagnosis,394 creative_diagnosis, landing_app_paths, activity_changelog,395 bottleneck_diagnosis → mcp_subagent_executor (full/deep), else native396Everything else → native_agent_mcp397```398399Subagent-to-native fallback is allowed for single tasks only. Every fallback must be400recorded in both `manifest.backend_fallbacks` and `source.attempts`.401402### Source Contract Extensions403404Every source file now carries:405- `backend`: `native_agent_mcp` or `mcp_subagent_executor`406- `auth_status`: empty for native/subagent sources; legacy bridge sources may carry auth state407- `attempts`: retry ladder records including backend fallback attempts408409### MCP Subagent Executor Constraints410411- **Agent-native only**: subagents call the existing TikTok MCP tools; Python never creates an MCP/OAuth client412- **Read-only for report flows**: no Create/Update/Status/Delete MCP tasks413- **Compact return contract**: subagents return only status, source paths, row counts, degraded sources, and errors414- **Output flow**: subagent fetches → `raw/*.json` → `normalize_mcp_source.py` → `sources/*.json`415- **Blocking first**: run only `blocking_before_report` shards for the first audited report. Shards marked416 `optional_after_first_report` (preview/activity enhancement) must not hold the main report unless the417 user explicitly asks for preview repair or enriched rerender.418- **Preview placeholders**: for `ad_details_for_enrichment` and `creative_preview_*`, run419 `prepare_preview_mcp_calls.py` after dependencies exist and execute the returned concrete MCP calls.420 Use `item_types`, `page`, and `page_size` for Spark posts; never use legacy `item_type` / `count`.421 If an image/video batch partially fails because of permissions, split and keep successful rows.422- **Legacy note**: `bridge_executor.py` remains only as deprecated compatibility glue and is not the new data plane423424#### Subagent Recovery Rules425426Real TikTok MCP runs showed that subagents may successfully call MCP but fail to write large427raw payloads to disk, or their final chat transcript may truncate/escape JSON. Treat the MCP428event stream as the source of truth:429430- A subagent must write and verify each planned `raw/*.json` immediately after the MCP call.431 If a raw file cannot be written in that step, stop the shard and recover; do not keep calling432 more sources.433- Check `raw/*.json` first; never trust a "completed" subagent message without files or434 recoverable MCP events.435- If files are missing, inspect the subagent Codex session JSONL and extract the MCP result436 from `mcp_tool_call_end.payload.result.Ok.content[0].text`.437- Prefer the automated recovery command before manual JSONL inspection:438 `python3 creatiads/scripts/recover_subagent_mcp_payloads.py --run-dir <run_dir>`.439- Prefer `mcp_tool_call_end` over `function_call_output`; the latter can be truncated or440 JSON-escaped into an invalid fragment for large report responses.441- Use `subagent_prompts/*.md` from `plan_subagent_execution.py` when spawning workers; the442 prompts are pre-scoped to each shard and reduce setup chatter.443- Do not wait more than a short window for a shard that already made MCP calls. Run the444 recovery command, rerun `workflow_runner.py`, then relaunch only genuinely missing sources.445- The main session may write recovered MCP JSON into `raw/*.json`; this still preserves the446 subagent execution model because the platform call happened inside the subagent.447- For large sources, use small page sizes or page-range shards only when event recovery is448 unavailable. Otherwise one complete direct report call is acceptable if `mcp_tool_call_end`449 contains the full payload.450- Close subagents after their result has been recovered or recorded.451452### Backend Consistency Checks453454`validate_pull_state.py` enforces:4551. All tasks in the same phase must use the same backend4562. Executor auth failures (`auth_required`/`permission_denied`) must not be masked by `status: ok`4573. Manifest fallback entries must have corresponding source attempts4584. `audit_creatiads_report.py` checks for MCP session metadata leaks and missing backend fields