Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "launchdarkly-ai-tooling",
"description": "Official LaunchDarkly plugins for Claude Code",
"owner": {
"name": "LaunchDarkly",
"email": "support@launchdarkly.com",
Expand Down
21 changes: 19 additions & 2 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "launchdarkly",
"version": "1.0.0",
"version": "1.0.1",
"description": "LaunchDarkly agent skills and MCP servers for feature flag management, AgentControl, and metrics",
"author": {
"name": "LaunchDarkly",
Expand All @@ -9,7 +9,8 @@
"homepage": "https://launchdarkly.com",
"repository": "https://github.com/launchdarkly/ai-tooling",
"license": "Apache-2.0",
"logo": "logo.svg",
"privacyPolicyUrl": "https://launchdarkly.com/policies/privacy/",
"icon": "logo.svg",
"keywords": [
"launchdarkly",
"feature-flags",
Expand All @@ -19,5 +20,21 @@
"rollout",
"cleanup",
"mcp"
],
"skills": [
"./skills/agentcontrol/configs-create",
"./skills/agentcontrol/configs-targeting",
"./skills/agentcontrol/configs-update",
"./skills/agentcontrol/configs-variations",
"./skills/feature-flags/launchdarkly-flag-cleanup",
"./skills/feature-flags/launchdarkly-flag-create",
"./skills/feature-flags/launchdarkly-flag-discovery",
"./skills/feature-flags/launchdarkly-flag-targeting",
"./skills/metrics/launchdarkly-metric-choose",
"./skills/metrics/launchdarkly-metric-create",
"./skills/metrics/launchdarkly-metric-instrument",
"./skills/agentcontrol/online-evals",
"./skills/agentcontrol/projects",
"./skills/agentcontrol/tools"
Comment thread
erangeles marked this conversation as resolved.
]
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ All notable changes to this repository will be documented in this file.

## Unreleased

- Claude Code plugin 1.0.1: resolve plugin directory scanner findings. Replace the flat `skills/` symlinks with an explicit `skills` list in `.claude-plugin/plugin.json`, add `icon` and `privacyPolicyUrl`, and stop skills from auto-detecting API tokens in environment variables or `~/.claude/config.json` (prefer the MCP server's OAuth; ask the user for a token when a REST call is unavoidable).
- Refine `experiments/launchdarkly-experiment-setup` skill to match the LaunchDarkly REST API tool shapes: nested `iteration` on create, `mutableFieldsByStatus`-aware `update-experiment`, and the new `save-and-start-experiment-iteration` + `stop-experiment-iteration` tools.
- Initial public release of LaunchDarkly agent skills
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ max_search_results = ai_config.model.get_custom("max_search_results") or 10

**MCP caveat — two paths, pick one.** The LaunchDarkly MCP `update-ai-config-variation` tool does not currently expose the top-level `custom` field on a variation. You have two options:

*Option A — PATCH via REST API.* Cleanest shape (value lands at `model.custom` where the Python/Node SDKs expose it via `get_custom(...)` / `custom` accessors) but requires a separate `LD_API_KEY` with write scope:
*Option A — PATCH via REST API.* Cleanest shape (value lands at `model.custom` where the Python/Node SDKs expose it via `get_custom(...)` / `custom` accessors) but requires an API access token with write scope (ask the user for it; do not read it from env vars or config files):

```bash
curl -X PATCH \
"https://app.launchdarkly.com/api/v2/projects/$PROJECT/ai-configs/$CONFIG_KEY/variations/$VARIATION_ID" \
-H "Authorization: $LD_API_KEY" \
-H "Authorization: {api_token}" \
-H "Content-Type: application/json" \
-d '{"patch":[{"op":"add","path":"/model/custom","value":{"max_search_results":10}}]}'
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import os

def get_ai_config_metrics(project_key: str, config_key: str, env: str = "production", hours: int = 24):
"""Get config metrics for the last N hours."""
API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")
API_TOKEN = "{api_token}" # token the user provided for this session

now = int(time.time())
start = now - (hours * 3600)
Expand Down
4 changes: 2 additions & 2 deletions skills/agentcontrol/configs-create/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,14 @@ Print this checklist verbatim to the user after Step 4, then wait for confirmati
Get the variation ID (use `get-ai-config` MCP, or):
```bash
curl -s "https://app.launchdarkly.com/api/v2/projects/$PROJECT/ai-configs/$CONFIG_KEY/targeting?env=$ENV" \
-H "Authorization: $LD_API_KEY" -H "LD-API-Version: beta" \
-H "Authorization: {api_token}" -H "LD-API-Version: beta" \
| jq '.variations[] | {key, _id}'
```

Flip the fallthrough to point at it:
```bash
curl -X PATCH "https://app.launchdarkly.com/api/v2/projects/$PROJECT/ai-configs/$CONFIG_KEY/targeting?env=$ENV" \
-H "Authorization: $LD_API_KEY" \
-H "Authorization: {api_token}" \
-H "Content-Type: application/json; domain-model=launchdarkly.semanticpatch" \
-H "LD-API-Version: beta" \
-d '{"instructions":[{"kind":"updateFallthroughVariationOrRollout","variationId":"<id-from-step-above>"}]}'
Expand Down
6 changes: 2 additions & 4 deletions skills/agentcontrol/configs-targeting/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ Configure targeting rules for configs to control which variations serve to diffe
- Project key and environment key
- Existing config with variations (use `configs-create` skill)

## API Key Detection
## API Access

1. **Check environment variables** - `LAUNCHDARKLY_API_KEY`, `LAUNCHDARKLY_API_TOKEN`, `LD_API_KEY`
2. **Check MCP config** - Claude: `~/.claude/config.json` -> `mcpServers.launchdarkly.env.LAUNCHDARKLY_API_KEY`
3. **Prompt user** - Only if detection fails
Prefer the LaunchDarkly MCP server tools: they authenticate via OAuth and need no token. If a step has no MCP equivalent and you must call the REST API directly, ask the user to paste an API access token for this session and substitute it for `{api_token}` in the examples below. Do not search environment variables, `.env` files, or agent/MCP config files for credentials.

## Core Concepts

Expand Down
46 changes: 8 additions & 38 deletions skills/agentcontrol/custom-metrics/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,9 @@ Full lifecycle management of custom business metrics: create metric definitions
- LaunchDarkly API token with `writer` role for metric management
- Understanding of built-in agent metrics (see `built-in-metrics`)

## API Key Detection
## API Access

Before prompting the user for an API key, try to detect it automatically:

1. **Check Claude MCP config** - Read `~/.claude/config.json` and look for `mcpServers.launchdarkly.env.LAUNCHDARKLY_API_KEY`
2. **Check environment variables** - Look for `LAUNCHDARKLY_API_KEY`, `LAUNCHDARKLY_API_TOKEN`, or `LD_API_KEY`
3. **Prompt user** - Only if detection fails, ask the user for their API key

```python
import os
import json
from pathlib import Path

def get_launchdarkly_api_key():
"""Auto-detect LaunchDarkly API key from Claude config or environment."""
# 1. Check Claude MCP config
claude_config = Path.home() / ".claude" / "config.json"
if claude_config.exists():
try:
config = json.load(open(claude_config))
api_key = config.get("mcpServers", {}).get("launchdarkly", {}).get("env", {}).get("LAUNCHDARKLY_API_KEY")
if api_key:
return api_key
except (json.JSONDecodeError, IOError):
pass

# 2. Check environment variables
for var in ["LAUNCHDARKLY_API_KEY", "LAUNCHDARKLY_API_TOKEN", "LD_API_KEY"]:
if os.environ.get(var):
return os.environ[var]

return None
```
Prefer the LaunchDarkly MCP server tools: they authenticate via OAuth and need no token. If a step has no MCP equivalent and you must call the REST API directly, ask the user to paste an API access token for this session and substitute it for `{api_token}` in the examples below. Do not search environment variables, `.env` files, or agent/MCP config files for credentials.

## Metrics Lifecycle Overview

Expand Down Expand Up @@ -86,7 +56,7 @@ def create_metric(
description: str = None
):
"""Create a new metric definition in LaunchDarkly."""
API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")
API_TOKEN = "{api_token}" # token the user provided for this session

url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}"

Expand Down Expand Up @@ -229,7 +199,7 @@ def track_revenue(ld_client, user_id: str, revenue: float, source: str):
```python
def get_metric(project_key: str, metric_key: str):
"""Get a single metric definition."""
API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")
API_TOKEN = "{api_token}" # token the user provided for this session

url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}/{metric_key}"

Expand Down Expand Up @@ -258,7 +228,7 @@ def get_metric(project_key: str, metric_key: str):
```python
def list_metrics(project_key: str, limit: int = 20):
"""List all metrics in a project."""
API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")
API_TOKEN = "{api_token}" # token the user provided for this session

url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}"

Expand Down Expand Up @@ -291,7 +261,7 @@ def update_metric(project_key: str, metric_key: str, updates: list):
updates: List of patch operations, e.g.:
[{"op": "replace", "path": "/name", "value": "New Name"}]
"""
API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")
API_TOKEN = "{api_token}" # token the user provided for this session

url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}/{metric_key}"

Expand Down Expand Up @@ -330,7 +300,7 @@ def rename_metric(project_key: str, metric_key: str, new_name: str, new_descript
```python
def delete_metric(project_key: str, metric_key: str):
"""Delete a metric from the project."""
API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")
API_TOKEN = "{api_token}" # token the user provided for this session

url = f"https://app.launchdarkly.com/api/v2/metrics/{project_key}/{metric_key}"

Expand Down Expand Up @@ -359,7 +329,7 @@ from ldclient.config import Config
import ldclient

# Setup
API_TOKEN = os.environ.get("LAUNCHDARKLY_API_TOKEN")
API_TOKEN = "{api_token}" # token the user provided for this session
SDK_KEY = os.environ.get("LAUNCHDARKLY_SDK_KEY")
PROJECT_KEY = "support-ai"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ Mirror the same shape on the LaunchDarkly variation. **MCP caveat.** The `update
```bash
curl -X PATCH \
"https://app.launchdarkly.com/api/v2/projects/$PROJECT/ai-configs/$CONFIG_KEY/variations/$VARIATION_ID" \
-H "Authorization: $LD_API_KEY" \
-H "Authorization: {api_token}" \
-H "Content-Type: application/json" \
-d '{"patch":[{"op":"add","path":"/model/custom","value":{"max_search_results":10}}]}'
```
Expand Down
6 changes: 2 additions & 4 deletions skills/agentcontrol/online-evals/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ Attach judges to config variations for automatic quality scoring using LLM-as-a-
- Existing config with variations (use `configs-create` skill)
- For automatic metric recording and the consolidated judge-result API: Python AI SDK v0.20.0+ or Node.js AI SDK v0.20.0+

## API Key Detection
## API Access

1. **Check environment variables** - `LAUNCHDARKLY_API_KEY`, `LAUNCHDARKLY_API_TOKEN`, `LD_API_KEY`
2. **Check MCP config** - Claude: `~/.claude/config.json` -> `mcpServers.launchdarkly.env.LAUNCHDARKLY_API_KEY`
3. **Prompt user** - Only if detection fails
Prefer the LaunchDarkly MCP server tools: they authenticate via OAuth and need no token. If a step has no MCP equivalent and you must call the REST API directly, ask the user to paste an API access token for this session and substitute it for `{api_token}` in the examples below. Do not search environment variables, `.env` files, or agent/MCP config files for credentials.

## Core Concepts

Expand Down
8 changes: 2 additions & 6 deletions skills/agentcontrol/projects/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,9 @@ You're using a skill that will guide you through setting up LaunchDarkly project
3. **Follow Conventions**: Respect existing code style and structure.
4. **Verify Integration**: Confirm the setup works: the agent performs checks and reports results.

## API Key Detection
## API Access

Before prompting the user for an API key, try to detect it automatically:

1. **Check environment variables**: Look for `LAUNCHDARKLY_API_KEY`, `LAUNCHDARKLY_API_TOKEN`, or `LD_API_KEY`
2. **Check MCP config**: If using Claude, read `~/.claude/config.json` for `mcpServers.launchdarkly.env.LAUNCHDARKLY_API_KEY`
3. **Prompt user**: Only if detection fails, ask the user for their API key
Prefer the LaunchDarkly MCP server tools: they authenticate via OAuth and need no token. If a step has no MCP equivalent and you must call the REST API directly, ask the user to paste an API access token for this session and substitute it for `{api_token}` in the examples below. Do not search environment variables, `.env` files, or agent/MCP config files for credentials.

See [Quick Start](references/quick-start.md) for API usage patterns.

Expand Down
1 change: 0 additions & 1 deletion skills/configs-create

This file was deleted.

1 change: 0 additions & 1 deletion skills/configs-targeting

This file was deleted.

1 change: 0 additions & 1 deletion skills/configs-update

This file was deleted.

1 change: 0 additions & 1 deletion skills/configs-variations

This file was deleted.

1 change: 0 additions & 1 deletion skills/launchdarkly-flag-cleanup

This file was deleted.

1 change: 0 additions & 1 deletion skills/launchdarkly-flag-create

This file was deleted.

1 change: 0 additions & 1 deletion skills/launchdarkly-flag-discovery

This file was deleted.

1 change: 0 additions & 1 deletion skills/launchdarkly-flag-targeting

This file was deleted.

1 change: 0 additions & 1 deletion skills/launchdarkly-metric-choose

This file was deleted.

1 change: 0 additions & 1 deletion skills/launchdarkly-metric-create

This file was deleted.

1 change: 0 additions & 1 deletion skills/launchdarkly-metric-instrument

This file was deleted.

1 change: 0 additions & 1 deletion skills/online-evals

This file was deleted.

1 change: 0 additions & 1 deletion skills/projects

This file was deleted.

1 change: 0 additions & 1 deletion skills/tools

This file was deleted.

Loading