feat(demographics): add age/gender/parental/income targeting tools#38
Open
chrmoller wants to merge 4 commits into
Open
feat(demographics): add age/gender/parental/income targeting tools#38chrmoller wants to merge 4 commits into
chrmoller wants to merge 4 commits into
Conversation
Contributor
chrmoller
commented
May 22, 2026
- get_demographic_targeting (read): list existing demographic criteria on an ad group or campaign, with a composite remove_id ready for remove_entity
- draft_demographic_targeting (write): propose exclusion (default) or positive demographic criteria. Accepts canonical enums and human-readable aliases (e.g. "25-34", "female", "top-10"). Surfaces warnings for near-total-dimension exclusions and positive narrowing.
- _apply_add_demographic_criteria: applies AGE_RANGE / GENDER / PARENTAL_STATUS / INCOME_RANGE criteria at ad-group or campaign level
- remove_entity: accepts ad_group_criterion and campaign_criterion as semantic aliases so removals read sensibly in plans and audit logs
- Orchestration rules document the bucket mismatch case (e.g. "23-35" has no exact mapping) and the audience-vs-demographic distinction
- get_demographic_targeting (read): list existing demographic criteria on an ad group or campaign, with a composite remove_id ready for remove_entity - draft_demographic_targeting (write): propose exclusion (default) or positive demographic criteria. Accepts canonical enums and human-readable aliases (e.g. "25-34", "female", "top-10"). Surfaces warnings for near-total-dimension exclusions and positive narrowing. - _apply_add_demographic_criteria: applies AGE_RANGE / GENDER / PARENTAL_STATUS / INCOME_RANGE criteria at ad-group or campaign level - remove_entity: accepts ad_group_criterion and campaign_criterion as semantic aliases so removals read sensibly in plans and audit logs - Orchestration rules document the bucket mismatch case (e.g. "23-35" has no exact mapping) and the audience-vs-demographic distinction Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The draft_demographic_targeting tool used raw list[str] | None for its four demographic-list parameters, which skipped the project's JSON-string-to-list coercion (the _StrListOpt validator introduced in kLOsk#28). MCP clients that pre-serialize list params as JSON strings — e.g. age_ranges="[\"25-34\"]" — hit Pydantic's list_type error. Switch all four to _StrListOpt to match the convention used by every other list-accepting tool in server.py (geo_target_ids, language_ids, keywords, callouts, image_paths, campaign_ids, etc.). Bare-scalar inputs like "65+" still fail by design — clients must send a real array or a JSON-encoded list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same root cause as the draft_demographic_targeting fix (642d870): the headlines and descriptions params used raw list[str | dict] instead of going through the project's JSON-string-to-list coercion validator. Add a new _StrOrDictList alias (no existing one fits because the RSA params accept mixed string-or-dict entries for unpinned vs pinned assets) and apply it. Add a regression test that submits JSON-encoded list strings — the same failure mode users would hit with pre-serializing MCP clients. Audit complete: every other list-accepting MCP tool in server.py already uses _StrList/_StrListOpt/_DictList/_DictListOpt, so this is the last remaining instance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The four demographic list params (age_ranges, genders,
parental_statuses, income_ranges) were declared as
`_StrListOpt = None`, which Pydantic encodes as
`{"anyOf": [{"type": "array"}, {"type": "null"}], "default": null}`.
That's spec-correct but some MCP clients only inspect the top-level
`type` field and never traverse `anyOf`, so they see no `type: array`
and refuse to serialize a list. Practical symptom: clients send the
list as a bare string and the Pydantic validator rejects it.
Switch to `_StrList = []` (with the same noqa: B006 comment used by
discover_keywords' seed_keywords). The resulting schema is flat:
`{"type": "array", "items": {...}, "default": []}` — every client
sees `type: array` and serializes correctly. The "at least one
dimension must be non-empty" validation already works with `[]` as
the empty sentinel (no change needed).
This is a targeted fix; the same pattern exists on other tools
(`get_recommendations.recommendation_types`,
`attribution_check.conversion_events`, etc.) and would benefit
from the same cleanup — left for a follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.