Skills
Skill 16 of 56
Configure config targeting rules to control which variations serve to different users.
3 minutes · 622 words · 31 sections
Install
npx skills add launchdarkly/ai-tooling --skill configs-targetingnpx skills add launchdarkly/ai-tooling/plugin marketplace add launchdarkly/ai-toolingThe first command installs just this skill, by the name in its SKILL.md; the second installs the whole repository.
Configure targeting rules for configs to control which variations serve to different contexts. Works the same for both completion and agent mode.
configs-create skill)Use the LaunchDarkly MCP server tools: they authenticate via OAuth and need no token. The REST examples under Scripting without MCP are for code that runs without the agent; if the user wants one run in this session, ask them to paste an API access token and substitute it for {api_token}. Do not search environment variables, .env files, or agent/MCP config files for credentials.
Targeting rules evaluate in this order (same as feature flags):
targetingInstructions and fallthrough take variationKey (a variation key or name) or variationId (a UUID from the targeting read). Raw REST needs the UUID.Every write below is one update-agent-config call with resource: "targeting" and requires confirm: true. Show the user the change and pass it only after they agree. A single call may combine on, fallthrough, and targetingInstructions.
Call get-agent-configs with projectKey, configKey, section: "targeting", and env. The response lists the variations (key, name, and _id), the rules with their _ids, individual targets, and the current fallthrough.
Set the default rule to the variation you created with fallthrough:
{
"projectKey": "my-project",
"configKey": "chat-assistant",
"resource": "targeting",
"env": "production",
"fallthrough": {"variationKey": "default"},
"confirm": true
}For a percentage split, pass fallthrough as {"weights": [{"variationKey": "default", "percent": 80}, {"variationKey": "experimental", "percent": 20}]}; the percents sum to 100. Add contextKind or bucketBy beside weights to change what is bucketed.
Important: The
turnTargetingOninstruction does not work for configs. A new config serves its disabled variation until you set the default rule.
Use targetingInstructions. It accepts addRule, removeRule, replaceRules, reorderRules, updateRuleDescription, addClauses, removeClauses, addValuesToClause, removeValuesFromClause, and updateClause.
Attribute-based rule:
{
"projectKey": "my-project",
"configKey": "model-selector",
"resource": "targeting",
"env": "production",
"targetingInstructions": [{
"kind": "addRule",
"clauses": [{
"contextKind": "user",
"attribute": "selectedModel",
"op": "contains",
"values": ["sonnet"],
"negate": false
}],
"variationKey": "sonnet"
}],
"comment": "Route sonnet requests",
"confirm": true
}Percentage rollout on a rule: send an addRule instruction with percentageRolloutConfig in place of variationKey, as in the Manual Percentage Rollout shape below.
Segment rule: a clause with attribute: "segmentMatch", op: "segmentMatch", an empty contextKind, and the segment keys as values.
Use targetingInstructions with addTargets, removeTargets, replaceTargets, or clearTargets. addTargets and removeTargets require contextKind and values:
{
"projectKey": "my-project",
"configKey": "chat-assistant",
"resource": "targeting",
"env": "production",
"targetingInstructions": [{"kind": "addTargets", "contextKind": "user", "variationKey": "experimental", "values": ["user-123"]}],
"confirm": true
}Call get-agent-configs with section: "targeting" again and confirm the fallthrough, rules, and targets match what the user approved.
For CI or scripts that can’t use the MCP server, the same steps go through the REST API’s semantic patch endpoint:
PATCH /api/v2/projects/{projectKey}/ai-configs/{configKey}/targeting
Content-Type: application/json; domain-model=launchdarkly.semanticpatchimport requests
import os
from typing import Dict, List, Optional
class AIConfigTargeting:
"""Manager for config targeting rules"""
def __init__(self, api_token: str, project_key: str):
self.api_token = api_token
self.project_key = project_key
self.base_url = "https://app.launchdarkly.com/api/v2"
def get_targeting(self, config_key: str) -> Optional[Dict]:
"""Get current targeting with variation IDs."""
url = f"{self.base_url}/projects/{self.project_key}/ai-configs/{config_key}/targeting"
response = requests.get(url, headers={
"Authorization": self.api_token,
"LD-API-Version": "beta"
})
if response.status_code == 200:
return response.json()
print(f"[ERROR] {response.status_code}: {response.text}")
return None
def get_variation_id(self, config_key: str, variation_key: str) -> Optional[str]:
"""Look up variation UUID from key or name."""
targeting = self.get_targeting(config_key)
if targeting:
for var in targeting.get("variations", []):
if var.get("key") == variation_key or var.get("name") == variation_key:
return var.get("_id")
return None
def update_targeting(self, config_key: str, environment: str,
instructions: List[Dict], comment: str = "") -> Optional[Dict]:
"""Send semantic patch instructions."""
url = f"{self.base_url}/projects/{self.project_key}/ai-configs/{config_key}/targeting"
payload = {"environmentKey": environment, "instructions": instructions}
if comment:
payload["comment"] = comment
response = requests.patch(url, headers={
"Authorization": self.api_token,
"Content-Type": "application/json; domain-model=launchdarkly.semanticpatch",
"LD-API-Version": "beta"
}, json=payload)
if response.status_code == 200:
return response.json()
print(f"[ERROR] {response.status_code}: {response.text}")
return None
def enable_config(self, config_key: str, environment: str,
variation_key: str = "default") -> bool:
"""
Enable a config by setting fallthrough to an enabled variation.
Note: turnTargetingOn doesn't work for configs. Instead, set the
fallthrough from the disabled variation (index 0) to an enabled one.
"""
variation_id = self.get_variation_id(config_key, variation_key)
if not variation_id:
print(f"[ERROR] Variation '{variation_key}' not found")
return False
return self.set_fallthrough(config_key, environment, variation_id)
def add_rule(self, config_key: str, environment: str,
clauses: List[Dict], variation: int,
description: str = "") -> bool:
"""Add targeting rule serving a specific variation index."""
instruction = {
"kind": "addRule",
"clauses": clauses,
"variation": variation
}
if description:
instruction["description"] = description
result = self.update_targeting(config_key, environment,
[instruction], f"Add rule: {description}")
if result:
print(f"[OK] Rule added")
return True
return False
def add_rollout_rule(self, config_key: str, environment: str,
clauses: List[Dict],
weights: List[Dict],
bucket_by: str = "key") -> bool:
"""
Add percentage rollout rule.
weights: [{"variation": 0, "weight": 50000}, {"variation": 1, "weight": 50000}]
"""
result = self.update_targeting(config_key, environment, [{
"kind": "addRule",
"clauses": clauses,
"percentageRolloutConfig": {
"contextKind": "user",
"bucketBy": bucket_by,
"variations": weights
}
}], "Add percentage rollout")
if result:
print(f"[OK] Rollout rule added")
return True
return False
def set_fallthrough(self, config_key: str, environment: str,
variation_id: str) -> bool:
"""Set default (fallthrough) variation by UUID."""
result = self.update_targeting(config_key, environment, [{
"kind": "updateFallthroughVariationOrRollout",
"variationId": variation_id
}], "Set fallthrough")
if result:
print(f"[OK] Fallthrough set")
return True
return False
def target_individuals(self, config_key: str, environment: str,
context_keys: List[str], variation: int,
context_kind: str = "user") -> bool:
"""Target specific context keys."""
result = self.update_targeting(config_key, environment, [{
"kind": "addTargets",
"variation": variation,
"contextKind": context_kind,
"values": context_keys
}], f"Target {len(context_keys)} individuals")
if result:
print(f"[OK] Individual targets added")
return True
return False
def target_segment(self, config_key: str, environment: str,
segment_keys: List[str], variation: int) -> bool:
"""Target a segment."""
result = self.update_targeting(config_key, environment, [{
"kind": "addRule",
"clauses": [{
"attribute": "segmentMatch",
"contextKind": "", # Leave blank for segments
"op": "segmentMatch",
"values": segment_keys,
"negate": False
}],
"variation": variation
}], f"Target segments: {segment_keys}")
if result:
print(f"[OK] Segment targeting added")
return True
return False
def clear_rules(self, config_key: str, environment: str) -> bool:
"""Remove all targeting rules."""
result = self.update_targeting(config_key, environment,
[{"kind": "replaceRules", "rules": []}], "Clear all rules")
if result:
print(f"[OK] All rules cleared")
return True
return FalseNote:
turnTargetingOnandturnTargetingOffdo not work for configs. Configs have targeting enabled by default. To “enable” a config, set the fallthrough to an enabled variation usingupdateFallthroughVariationOrRollout.
| Kind | Description |
|---|---|
addRule | Add rule with clauses and variation/rollout |
removeRule | Remove by ruleId |
replaceRules | Replace all rules |
reorderRules | Change evaluation order |
updateRuleVariationOrRollout | Update what a rule serves |
| Kind | Description |
|---|---|
updateFallthroughVariationOrRollout | Set default variation or rollout |
| Kind | Description |
|---|---|
addTargets | Target specific context keys |
removeTargets | Remove specific targets |
replaceTargets | Replace all targets |
| Operator | Description | Example |
|---|---|---|
in | Value in list | ["premium", "enterprise"] |
contains | String contains | ["sonnet"] |
startsWith | String prefix | ["user-"] |
endsWith | String suffix | [".edu"] |
matches | Regex match | ["^user-\\d+$"] |
greaterThan / lessThan | Numeric comparison | [100] |
before / after | Date comparison | ["2024-12-31T00:00:00Z"] |
semVerEqual / semVerGreaterThan | Version comparison | ["2.0.0"] |
segmentMatch | Segment membership | ["beta-testers"] |
{
"contextKind": "user",
"attribute": "email",
"op": "endsWith",
"values": [".edu"],
"negate": false
}negate: true inverts the operator{
"percentageRolloutConfig": {
"contextKind": "user",
"bucketBy": "key",
"variations": [
{"variation": 0, "weight": 50000},
{"variation": 1, "weight": 50000}
]
}
}{
"progressiveRolloutConfig": {
"contextKind": "user",
"controlVariation": 1,
"endVariation": 0,
"steps": [
{"rolloutWeight": 1000, "duration": {"quantity": 4, "unit": "hour"}},
{"rolloutWeight": 5000, "duration": {"quantity": 4, "unit": "hour"}},
{"rolloutWeight": 10000, "duration": {"quantity": 4, "unit": "hour"}}
]
}
}{
"guardedRolloutConfig": {
"randomizationUnit": "user",
"stages": [
{"rolloutWeight": 1000, "monitoringWindowMilliseconds": 17280000},
{"rolloutWeight": 5000, "monitoringWindowMilliseconds": 17280000}
],
"metrics": [{
"metricKey": "error-rate",
"onRegression": {"rollback": true},
"regressionThreshold": 0.01
}]
}
}# Route based on selectedModel context attribute
targeting.add_rule(
config_key="model-selector",
environment="production",
clauses=[{
"contextKind": "user",
"attribute": "selectedModel",
"op": "contains",
"values": ["sonnet"],
"negate": False
}],
variation=0, # Sonnet variation index
description="Route sonnet requests"
)targeting.add_rule(
config_key="chat-assistant",
environment="production",
clauses=[{
"contextKind": "user",
"attribute": "tier",
"op": "in",
"values": ["premium", "enterprise"],
"negate": False
}],
variation=0 # Premium model variation
)targeting.target_segment(
config_key="chat-assistant",
environment="production",
segment_keys=["beta-testers"],
variation=1 # Experimental variation
)| Status | Cause | Solution |
|---|---|---|
| 400 | Invalid semantic patch | Check instruction format, ops must be lowercase |
| 403 | Insufficient permissions | Check API token |
| 404 | Config not found | Verify projectKey and configKey |
| 422 | Invalid variation | Use index (0, 1, 2…) or UUID from targeting response |
After configuring targeting:
https://app.launchdarkly.com/projects/{projectKey}/ai-configs/{configKey}built-in-metricsonline-evalsconfigs-create - Create configs with variationsconfigs-variations - Manage variationsonline-evals - Attach judgessegments - Create segments for targetingConfigure config targeting rules to control which variations serve to different users. Enable percentage rollouts, attribute-based rules, segment targeting, and guarded rollouts.
The verbatim description from this skill’s front matter — the string an agent matches on to decide whether to load it.
skills/agentcontrol/configs-targeting/SKILL.mdmain, last pushed 7 November 2026.SKILL.md, not by matching a directory convention. 10 distinct layouts observed: skills/agentcontrol/*/SKILL.md, skills/aws-devops-agent/*/SKILL.md, skills/experiments/*/SKILL.md, skills/feature-flags/*/SKILL.md, skills/metrics/*/SKILL.md, skills/observability/*/SKILL.md, skills/onboarding/*/SKILL.md, skills/onboarding/sdk-install/*/SKILL.md, skills/*/SKILL.md, skills/release-autopilot/*/SKILL.md.h1 and no skipped levels:.claude-plugin/marketplace.json by LaunchDarkly, declaring 1 plugin. It is read for editorial metadata only — never as the skill index, which is always the repository tree./launchdarkly/ai-tooling.md, and each skill at its own .md URL.1 file · 1 KB
Everything this skill ships beside its prose. All of it is set here, as a subchapter of skill 16.
Everything else published alongside the skill.