feat(onboarding): port the onboardingV2 skill and add its eval suite (#168)

* Port onboardingV2 skill into skills/onboarding and add its eval suite

* chore(onboarding): drop files unused by the v2 flow

Remove first-flag/, references/1.8-summary.md, and references/1.9-editor-rules.md
(the v2 flow has no summary or editor-rules step) and repoint every reference to
the parent skill's Step 4.

* fix(onboarding): resolve mcp/fm contradiction and Cursor toggle name

Drop the edge case claiming mcp/fm needs no migration, which contradicted the
adjacent deprecation prompt, and name the Cursor toggle after the server the
config actually writes.

* fix(evals): score onboarding against the real trajectory
This commit is contained in:
ari-launchdarkly
2026-09-03 06:27:19 -07:00
committed by GitHub
parent 5992d9eb7e
commit 45fcc2553c
18 changed files with 709 additions and 1034 deletions
+1 -2
View File
@@ -73,10 +73,9 @@ Once installed, skills are available as `/launchdarkly:<skill-name>` across all
| Skill | Description |
|-------|-------------|
| `onboarding` | End-to-end LaunchDarkly setup: kickoff roadmap, MCP, SDK install, first flag |
| `onboarding` | End-to-end LaunchDarkly setup: quiet scripted flow, SDK install, first flag with a live reveal, MCP offered afterwards |
| `onboarding/mcp-configure` | Configure the LaunchDarkly hosted MCP server (OAuth, no API keys needed) |
| `onboarding/sdk-install` | Install and initialize the correct SDK via detect, plan, and apply sub-steps |
| `onboarding/first-flag` | Create a boolean flag, evaluate it, toggle on/off for end-to-end proof |
## Install as a Cursor Plugin
+2
View File
@@ -33,6 +33,7 @@ npm run eval:configs-update # agentcontrol/configs-update
npm run eval:agentcontrol-tools # agentcontrol/tools
npm run eval:configs-variations # agentcontrol/configs-variations
npm run eval:flag-create # feature-flags/launchdarkly-flag-create
npm run eval:onboarding # onboarding
# Quick smoke check — first test case only (~15-20s, ~$0.05)
npm run eval:configs-create:single
@@ -40,6 +41,7 @@ npm run eval:configs-update:single
npm run eval:agentcontrol-tools:single
npm run eval:configs-variations:single
npm run eval:flag-create:single
npm run eval:onboarding:single
# Aggregate and CI operations
npm run eval:all # Run every suite and rebuild ../eval-scores.json
+195
View File
@@ -0,0 +1,195 @@
# yaml-language-server: $schema=https://promptfoo.dev/config-schema.json
#
# Run with shared defaults:
# promptfoo eval -c shared/defaults.yaml -c onboarding/promptfooconfig.yaml
#
# Scores the kickoff plus the early decision points of the onboarding skill,
# where the reported UX problems live. Lean mode (allow_builtins:false) keeps
# runs fast: the agent produces the scripted conversation using the mocked LD
# tools and ask-question instead of running a real install. max_turns is capped
# per test so a run stops after the beats being graded.
#
# Because file tools are off, `codebase_context` states its listing as the
# result of the repository scan. Without that framing the agent has no way to
# see an app and takes the skill's "no runnable app" branch in every fixture,
# which is not the state under test.
description: "Evaluation of the onboarding skill: kickoff and early-decision UX"
prompts:
- file://../../skills/onboarding/SKILL.md
providers:
- id: file://../providers/claude-skill-agent-sdk.js
label: claude-skill-agent-sdk
config:
skill_slug: onboarding
allow_builtins: false
expose_mcp_tools: true
expose_ask_question: true
force_skill_invocation: true
tests:
# ------------------------------------------------------------------
# Test 1: Kickoff on a clear app. Short welcome, safety reassurance,
# no roadmap table, no internal leakage, decisions via the tool.
# ------------------------------------------------------------------
- description: "Kickoff (clear app): concise opener, asks via tool, no internal leak"
vars:
user_request: "Set up LaunchDarkly in my project."
codebase_context: >
Repository scan result (file tools are unavailable this run; treat this as what your scan
returned): a single-package Node.js + Express app on npm. package.json with express,
src/index.js as the entrypoint and server start. Git repository on branch main, clean tree.
No LaunchDarkly SDK dependency, no LaunchDarkly code, no MCP configuration.
max_turns: 6
mock_ask_question_answers:
- ["yes"]
- ["ld_ui"]
- ["done"]
assert:
- type: javascript
value: |
const tools = output.tools_called || [];
const pass = tools.some(t => String(t).includes('ask-question'));
return { pass, score: pass ? 1 : 0, reason: 'tools: ' + (tools.join(' -> ') || '(none)') };
metric: asks_via_tool
weight: 3
- type: llm-rubric
value: |
Evaluate `kickoff_text` (everything before the first tool call) against the skill's Kickoff and
Voice and Tone rules. Score 1.0 if ALL hold, deduct ~0.25 each:
1. Opens with a brief welcome (about two sentences) that says what onboarding will do and that no
code changes are committed without the user's approval. No "I'll help you" / "Let me" filler.
2. Plain status-line voice: short sentences, no slogans or metaphors, no em dashes.
3. Does not ask whether the user has a LaunchDarkly account, and does not print a roadmap table
of the stages.
4. Leaks NO internal machinery: no step labels ("Step 0/1/2"), skill or file names, workflow
jargon ("nested skill", "hand off", "BLOCKING"), or MCP/editor-restart rationale.
If `kickoff_text` is empty, score 0.
metric: kickoff_quality
weight: 3
# ------------------------------------------------------------------
# Test 2: Multi-package workspace is always "Unclear" in Step 1: the
# agent must ask which package to integrate instead of guessing.
# ------------------------------------------------------------------
- description: "Monorepo: asks which package (does not guess)"
vars:
user_request: "Add LaunchDarkly to my project."
codebase_context: >
Repository scan result (file tools are unavailable this run; treat this as what your scan
returned): a pnpm workspace monorepo. pnpm-workspace.yaml lists packages/web (Next.js app),
packages/api (Express service) and packages/shared (library, no entrypoint). Git repository
on branch main, clean tree. No LaunchDarkly SDK dependency and no LaunchDarkly code anywhere.
max_turns: 5
mock_ask_question_answers:
- ["packages/web"]
- ["yes"]
assert:
- type: javascript
value: |
const asks = (output.trajectory || []).filter(t => String(t.tool).includes('ask-question'));
if (!asks.length) return { pass: false, score: 0, reason: 'no ask-question call' };
const scored = asks.map(a => {
const opts = (a.arguments && a.arguments.options) || [];
const text = opts.map(o => (o.label||'')+' '+(o.id||'')).join(' | ').toLowerCase();
const pkgs = ['web', 'api', 'shared'].filter(p => new RegExp('\\b' + p + '\\b').test(text)).length;
return { opts: opts.length, pkgs, text };
});
const hit = scored.find(s => s.opts >= 2 && s.pkgs >= 2);
const best = hit || scored[0];
return {
pass: Boolean(hit),
score: hit ? 1 : 0,
reason: best.opts + ' options; package mentions=' + best.pkgs + ' || ' + best.text.slice(0,160),
};
metric: asks_which_package
weight: 3
- type: llm-rubric
value: |
The project is a multi-package monorepo. Per the skill's Step 1, the agent must NOT guess which
package to integrate: it should ask the user to choose among the candidate packages. Evaluate
`kickoff_text` plus the first question: does it surface the ambiguity and ask, rather than
assuming one package? Score 1.0 if it clearly asks the user to pick; 0 if it picks one and
proceeds.
metric: no_guess_on_monorepo
weight: 2
# ------------------------------------------------------------------
# Test 3: MCP is offered only after the first flag works, so nothing
# on the way to the flag may configure MCP or ask the user about it.
# ------------------------------------------------------------------
- description: "MCP: not configured or offered on the way to the first flag"
vars:
user_request: "Set up LaunchDarkly in my app."
codebase_context: >
Repository scan result (file tools are unavailable this run; treat this as what your scan
returned): a single-package React app built with Vite on npm, src/main.tsx entrypoint,
`npm run dev` serves it. Git repository on branch main, clean tree. No LaunchDarkly SDK
dependency, no LaunchDarkly code, and no MCP configuration in any editor config.
max_turns: 6
mock_ask_question_answers:
- ["yes"]
- ["ld_ui"]
assert:
# The MCP offer is expected at the end of the flow, so only the calls up to
# the flag's creation are checked: nothing on the way to the flag may
# configure MCP or raise it with the user.
- type: javascript
value: |
const traj = output.trajectory || [];
const flagIdx = traj.findIndex(t => String(t.tool) === 'create-flag');
const before = traj.slice(0, flagIdx === -1 ? traj.length : flagIdx);
const questions = before
.filter(t => String(t.tool).includes('ask-question'))
.map(t => JSON.stringify(t.arguments || {}).toLowerCase());
const asksAboutMcp = questions.some(q => q.includes('mcp'));
return {
pass: !asksAboutMcp,
score: asksAboutMcp ? 0 : 1,
reason: asksAboutMcp
? 'asked about MCP before creating the flag'
: questions.length + ' question(s) before create-flag@' + flagIdx + ', none about MCP',
};
metric: no_mcp_question_before_flag
weight: 3
- type: llm-rubric
value: |
Evaluate `kickoff_text` and `assistant_turns`. Per the skill, MCP is offered only after the first
flag works, and MCP internals are never explained to the user during setup. Score 1.0 if the
agent gets on with scanning and SDK install without mentioning MCP, editor restarts, or MCP
server configuration to the user. Score 0 if it walks the user through MCP setup or explains MCP
before the flag is working.
metric: mcp_deferred
weight: 2
# ------------------------------------------------------------------
# Test 4: Drift. The user tries to skip ahead; the skill requires
# acknowledge, concrete consequence, then a deliberate choice.
# ------------------------------------------------------------------
- description: "Drift: redirects a skip-ahead request with the tradeoff stated"
vars:
user_request: "Skip the setup, just give me the code to wrap my checkout button in a flag."
codebase_context: >
Repository scan result (file tools are unavailable this run; treat this as what your scan
returned): a single-package Next.js app in TypeScript on npm, with a checkout page under
app/checkout/page.tsx. Git repository on branch main, clean tree. No LaunchDarkly SDK
dependency and no LaunchDarkly code.
max_turns: 4
mock_ask_question_answers:
- ["install the SDK first"]
assert:
- type: llm-rubric
value: |
The user asked to skip onboarding and be handed flag code. Judge the agent's reply to that
request: the `assistant_turns` texts together with the prompt and options of the
`ask-question` calls in `trajectory` (the reply's choice is offered through that tool, so a
question offering "install the SDK first" versus "give me the code now" counts as the choice).
Per the skill's Redirecting Drift rules the reply must do three things, in order: acknowledge
the request in the user's words, name in one sentence the concrete consequence of skipping (for
example that the flag calls will not run until the SDK is installed), and offer the choice
between finishing the quick step first or proceeding their way. Score 1.0 when all three are
present; deduct for silent compliance (dumping code with no tradeoff), rigid refusal, or a
vague consequence.
metric: handles_drift
weight: 2
+2
View File
@@ -23,6 +23,8 @@
"eval:flag-release:single": "promptfoo eval -c shared/defaults.yaml -c flag-release/promptfooconfig.yaml --env-file .env --no-cache --filter-first-n 1",
"eval:flag-and-release-change": "promptfoo eval -c shared/defaults.yaml -c flag-and-release-change/promptfooconfig.yaml --env-file .env --no-cache -o flag-and-release-change/results.json",
"eval:flag-and-release-change:single": "promptfoo eval -c shared/defaults.yaml -c flag-and-release-change/promptfooconfig.yaml --env-file .env --no-cache --filter-first-n 1",
"eval:onboarding": "promptfoo eval -c shared/defaults.yaml -c onboarding/promptfooconfig.yaml --env-file .env --no-cache -o onboarding/results.json",
"eval:onboarding:single": "promptfoo eval -c shared/defaults.yaml -c onboarding/promptfooconfig.yaml --env-file .env --no-cache --filter-first-n 1",
"fixture:from-pr": "node scripts/build-pr-fixture.js",
"eval:all": "node scripts/aggregate.js --run",
"eval:aggregate": "node scripts/aggregate.js",
+6
View File
@@ -73,6 +73,12 @@ const SUITES = [
skillDir: "skills/feature-flags/flag-and-release-change",
readme: "skills/feature-flags/flag-and-release-change/README.md",
},
{
suite: "onboarding",
skillKey: "onboarding",
skillDir: "skills/onboarding",
readme: "skills/onboarding/README.md",
},
];
/**
+5 -13
View File
@@ -179,14 +179,6 @@
"license": "Apache-2.0",
"compatibility": "Requires access to the project repository"
},
{
"name": "first-flag",
"description": "Create a boolean first flag, add evaluation, toggle on/off for end-to-end proof. Parent onboarding Step 6; uses MCP, API, or ldcli; optional flag-create skill.",
"path": "skills/onboarding/first-flag",
"version": "0.1.0",
"license": "Apache-2.0",
"compatibility": "Requires SDK installed (parent Step 5) and LaunchDarkly project access"
},
{
"name": "flag-and-release-change",
"description": "Drive a pull request's change end to end: decide it's flag-worthy, create the guarding flag, wire the new code path behind it on the PR branch, and record an automated release so the change ships safely when the PR merges. A portable orchestrator that composes should-flag-change, launchdarkly-flag-create, and flag-release. Keywords: flag a PR, wrap change in a flag, dark launch, kill switch, auto-release, automated rollout, end-to-end flag workflow.",
@@ -410,7 +402,7 @@
},
{
"name": "mcp-configure",
"description": "Configure the LaunchDarkly hosted MCP server during onboarding. Use when the parent LaunchDarkly onboarding skill reaches Step 4 (MCP). Supports Cursor, Claude Code, Windsurf, GitHub Copilot, and other MCP-compatible agents. OAuth authentication; no API keys for the hosted server.",
"description": "Configure the LaunchDarkly hosted MCP server during onboarding. Use when the parent LaunchDarkly onboarding skill reaches the MCP offer, after the first flag works. Supports Cursor, Claude Code, Windsurf, GitHub Copilot, and other MCP-compatible agents. OAuth authentication; no API keys for the hosted server.",
"path": "skills/onboarding/mcp-configure",
"version": "0.1.0",
"license": "Apache-2.0",
@@ -426,11 +418,11 @@
},
{
"name": "onboarding",
"description": "Onboard a project to LaunchDarkly: kickoff roadmap, resumable log, explore repo, MCP, companion flag skills, nested SDK install (detect/plan/apply), first flag. Use when adding LaunchDarkly, setting up or integrating feature flags in a project, SDK integration, or 'onboard me'.",
"description": "Scripted onboarding for LaunchDarkly: quiet execution, fixed sequence, SDK install, first flag with a live reveal, MCP offered afterwards. Enforces step completion before advancing and redirects drift. Use when adding LaunchDarkly, setting up or integrating feature flags in a project, SDK integration, or 'onboard me'.",
"path": "skills/onboarding",
"version": "0.2.0",
"version": "0.3.0",
"license": "Apache-2.0",
"compatibility": "Requires an MCP-capable coding agent, `npx` on PATH for optional skill installs, and a LaunchDarkly account. SDK keys, client-side IDs, mobile keys, and API tokens are only needed when the step that uses them runs (see Prerequisites).",
"compatibility": "Requires an MCP-capable coding agent and `npx` on PATH for optional skill installs. SDK keys, client-side IDs, and mobile keys are only needed when the SDK key step runs.",
"tags": [
"launchdarkly",
"feature-flags",
@@ -466,7 +458,7 @@
},
{
"name": "sdk-install",
"description": "Install and initialize the correct LaunchDarkly SDK during onboarding by running nested skills in order: detect, plan, apply. Parent onboarding Step 6 is first flag.",
"description": "Install and initialize the correct LaunchDarkly SDK during onboarding by running nested skills in order: detect, plan, apply. Parent onboarding Step 4 is first flag.",
"path": "skills/onboarding/sdk-install",
"version": "0.2.0",
"license": "Apache-2.0",
+54
View File
@@ -0,0 +1,54 @@
# Onboarding Skill
An Agent Skill that onboards a project to LaunchDarkly end to end: install the SDK, create a first flag, and let the user watch it flip in their running app.
## Overview
The skill runs a fixed sequence and keeps the conversation quiet between decision points, so the user can delegate the work and come back to a finished state:
1. **Safe workspace** — create the `launchdarkly-onboarding` branch and leave every change uncommitted for the user to review.
2. **Explore** — classify the workspace from dependency manifests and entrypoints. A multi-package workspace is always ambiguous, so the skill asks which package to integrate instead of guessing. An empty workspace gets an offer to scaffold a small demo app.
3. **Install the SDK** — hand off to [`sdk-install`](sdk-install/SKILL.md) (detect, plan, apply), or use the fast path when the app was just scaffolded. The SDK key comes from MCP when it is already configured, otherwise from a direct dashboard link.
4. **First flag** — create the flag, add a flag-gated banner to the app, start the dev server, and hand the reveal to the user so they see the banner change when the flag turns on.
5. **MCP (optional)** — offered only after the flag works, via [`mcp-configure`](mcp-configure/SKILL.md).
Notable behaviors:
- **No log or summary file.** State is detected from the repository, so "continue onboarding" resumes at the first incomplete step.
- **Drift is redirected, not refused.** When the user asks to skip ahead, the skill acknowledges the request, names the concrete consequence in one sentence, and offers the choice.
- **No internal leakage.** Step labels, skill file names, workflow jargon, and MCP or SDK internals never reach the user.
## Installation
```bash
npx skills add launchdarkly/ai-tooling --skill onboarding -y --agent <agent>
```
Or copy `skills/onboarding/` into your client's skills path.
## Prerequisites
- An MCP-capable coding agent (Cursor, Claude Code, Windsurf, GitHub Copilot, and others).
- `npx` on PATH for optional companion skill installs.
- A LaunchDarkly account. SDK keys, client-side IDs, and mobile keys are only needed when the SDK key step runs.
## Usage
Ask the agent to onboard the project:
> Set up LaunchDarkly in my project.
## Nested skills
| Skill | Purpose |
|-------|---------|
| [`sdk-install`](sdk-install/SKILL.md) | Detect the stack, plan the integration, apply the code changes |
| [`mcp-configure`](mcp-configure/SKILL.md) | Configure the hosted LaunchDarkly MCP server |
## Evaluations
`evals/onboarding/` scores the kickoff and the early decision points: kickoff voice, monorepo ambiguity, MCP staying out of the way until the flag works, and drift handling.
```bash
cd evals && npm run eval:onboarding
```
+248 -273
View File
@@ -1,349 +1,324 @@
---
name: onboarding
description: "Onboard a project to LaunchDarkly: kickoff roadmap, resumable log, explore repo, MCP, companion flag skills, nested SDK install (detect/plan/apply), first flag. Use when adding LaunchDarkly, setting up or integrating feature flags in a project, SDK integration, or 'onboard me'."
description: "Scripted onboarding for LaunchDarkly: quiet execution, fixed sequence, SDK install, first flag with a live reveal, MCP offered afterwards. Enforces step completion before advancing and redirects drift. Use when adding LaunchDarkly, setting up or integrating feature flags in a project, SDK integration, or 'onboard me'."
license: Apache-2.0
compatibility: Requires an MCP-capable coding agent, `npx` on PATH for optional skill installs, and a LaunchDarkly account. SDK keys, client-side IDs, mobile keys, and API tokens are only needed when the step that uses them runs (see Prerequisites).
compatibility: Requires an MCP-capable coding agent and `npx` on PATH for optional skill installs. SDK keys, client-side IDs, and mobile keys are only needed when the SDK key step runs.
metadata:
author: launchdarkly
version: "0.2.0"
version: "0.3.0"
---
# LaunchDarkly SDK Onboarding
# LaunchDarkly Onboarding
Orchestrates LaunchDarkly setup in an existing codebase: on kickoff, show a **roadmap** in chat (see [Kickoff: onboarding roadmap](#kickoff-onboarding-roadmap)); **Step 0** writes a living onboarding log so a new session or the user can resume; then explore the project, detect the agent, install flag-management skills, **configure the LaunchDarkly MCP server early**, install and initialize the SDK (**sdk-install** and nested detect/plan/apply), and create a **first feature flag**. Nested skills: [mcp-configure](mcp-configure/SKILL.md), [sdk-install](sdk-install/SKILL.md), [first-flag](first-flag/SKILL.md).
## Voice and Tone
## Prerequisites
Write plainly. Say only what you did or are about to do. No flourishes, slogans, or metaphors.
- **`npx`:** Available on PATH when using `npx skills add` to install companion skills (see Step 3).
- **LaunchDarkly account (deferred -- inferred, not asked upfront):** Do NOT ask whether the user has a LaunchDarkly account at the start. Instead, let the workflow reveal account status naturally:
- **Steps 0-3** (log, explore, detect, install skills) do not require an account. Run them first.
- **Step 4 (MCP):** Present the MCP install link. If the user completes OAuth successfully, they have an account -- confirmed, no question needed.
- **Step 5 (SDK keys):** If MCP is not configured (or the user declined it), account status becomes relevant at D7 when the user needs to provide keys. If they cannot provide keys, offer the resolved signup link (see [Source Attribution](#source-attribution)).
- This eliminates the upfront "Do you have an account?" question and lets the workflow itself surface whether the user needs to sign up.
- **Keys and tokens (defer until needed):** Collect these only when the path requires them.
- **Step 4 -- MCP:** **Hosted MCP** uses OAuth; no API token or SDK key needed to configure it.
- **Step 5 -- SDK:** **SDK keys / client-side ID / mobile key** when wiring env in [Apply code changes](sdk-install/apply/SKILL.md), after the integration plan is confirmed. **`ldcli` / REST** for discovery: use **`ldcli login`** or an access token when you first run those commands, not at hello.
- **Key type must match the integration:** server-side SDK -> **SDK key**; browser/client-side SDK -> **Client-side ID**; mobile -> **Mobile key**. Env variable names and bundler rules: [Apply code changes](sdk-install/apply/SKILL.md).
- Status lines, not narration. "Scanning your project now." "Scan complete. Installing SDK."
- No em dashes. Use periods or commas.
- Never explain internal mechanics or rationale the user has no context for (MCP, editor restarts, how the SDK connects, "no redeploy"). It means nothing to a first-time user.
- Before writing any line, ask whether the user needs it. If not, cut it.
- Do not offer a choice the user cannot meaningfully make. If the right move is obvious, just do it.
- Reassure plainly about safety. "No code will be changed without your approval."
**MCP (preferred):** Complete **Step 4** via [mcp-configure/SKILL.md](mcp-configure/SKILL.md) before SDK work when possible. If MCP is unavailable or the user opts out, use **ldcli** / **REST** fallbacks described in that skill -- onboarding must still be completable.
## Source Attribution
**Optional MCP tools (when configured):**
The signup URL includes a `source` query parameter for attribution. Resolve it once at kickoff by scanning the user's original message. Store the resolved URL for the session. This marker is for the agent only. Never show it to the user.
- `get-environments` -- list environments for a project; the response includes SDK keys, client-side IDs, and mobile keys per environment. **Use this as the single source for all key types** -- do not make separate requests for individual keys.
- `create-feature-flag` -- create the boolean flag for [Step 6: Create Your First Feature Flag](#step-6-create-your-first-feature-flag).
- `update-feature-flag` -- toggle or patch flag configuration during Step 6; see [Create first feature flag](first-flag/SKILL.md) for ldcli/API fallbacks.
| User's original prompt contains | Source value | Resulting URL |
|---|---|---|
| `source-launchdarkly` | `ldwebsite` | `https://app.launchdarkly.com/signup?source=ldwebsite` |
| No marker | `agent` | `https://app.launchdarkly.com/signup?source=agent` |
**Other MCP tools you may use if present** (not required): `list-feature-flags`, `get-feature-flag`, `get-flag-status-across-environments`.
- Scan the user's initial message for `source-launchdarkly`. If found, use `ldwebsite`. Otherwise use `agent`.
- Parse once, before Step 0. Do not re-parse later.
- On resume, use `agent`.
## Agent Behavior Directives
Wherever these instructions say "offer the signup link," use the resolved URL. Never hardcode `?source=agent`.
### Source Attribution
## Rules
The signup URL used throughout onboarding includes a `source` query parameter for attribution. The default value is `agent`, producing `https://app.launchdarkly.com/signup?source=agent`. However, the source is **dynamic** based on the user's original prompt:
- The step labels below are your internal roadmap. Never show step names or numbers to the user.
- Enforce sequence. Do not advance until the current stage is done.
- Work silently between decision points. Do the work without narrating each step. The user may have walked away and should return to a finished state, not a wall of scroll.
- Speak only when you need the user: at the opening, a real decision point, a manual step you cannot do for them, or completion. Keep it short and lead with the outcome.
- Make changes on a branch and leave them uncommitted. The user reviews and commits, not you.
- Install companion skills at the point of need, never upfront.
- Never skip a stage unless the user already has that piece in place (verified, not assumed).
- If a stage fails, stop and resolve it before continuing.
- If the user's initial message (the prompt that triggered onboarding) contains the marker **`source-launchdarkly`**, set the source to **`ldwebsite`** → `https://app.launchdarkly.com/signup?source=ldwebsite`.
- Otherwise, keep the default source **`agent`** → `https://app.launchdarkly.com/signup?source=agent`.
### Speaking to the user
Detect this **once** at kickoff (before Step 0) by scanning the user's original message for the substring `source-launchdarkly`. Store the resolved signup URL for the session and use it wherever this skill or any nested skill directs the user to sign up. The marker is metadata for the agent — do not echo it back to the user or include it in any user-facing output.
Keep user-facing messages rare and short. You are letting the user delegate and walk away.
### Progress Tracking
- At the opening: a one-line welcome, what onboarding will do, and that nothing is committed without their okay. Then the first choice. Then go quiet and work.
- At a decision point or completion: one short summary. What happened, anything you need from them, and clear choices with a recommended default.
- Do not narrate routine work or write prose between execution steps. When the terminal stops scrolling, the user reads one concise message and never has to scroll back.
The roadmap (Steps 0-6 + follow-through) MUST be tracked using your agent's native task-tracking tool in addition to the onboarding log file.
### Forbidden in user-facing output
- **Cursor:** Use `TodoWrite` to create a todo for each step before beginning work. Update status as each step completes.
- **Claude Code:** Use `TaskCreate` to create a task for each step (or `TodoWrite` if native tasks are unavailable).
- **Other agents:** If your agent provides a native task list or progress tracking tool, use it. If not, present a numbered checklist in chat and update it after each step.
- Step names, internal labels, or skill file names
- Workflow language ("hand off," "proceed to next step")
- Internal rationale (MCP, editor restarts, SDK internals)
- Raw markdown from these instructions
- The app's rendered output or the flag-gated content. Point the user to the running app instead of pasting what it shows.
Do NOT work through steps mentally or rely solely on the `LAUNCHDARKLY_ONBOARDING.md` log for in-session tracking.
---
### Decision Points
## Experience Detection
When a step requires user input to determine branching, you MUST collect the answer by calling a tool — do NOT write the question as prose text in your response.
Greet and present the first choice immediately. Do not make the user wait on a scan. Start the codebase scan in the background (a subagent if available) and use its results when they land. Never ask the user about the scan.
**Call the tool directly.** Use the first tool from this list that
your environment provides:
| Signal | Inference |
|---|---|
| LD SDK in dependencies | Knows LaunchDarkly |
| `variation()`, `useFlags()`, or equivalent calls present | Has used flags before |
| MCP already configured | Familiar with the tooling |
| Well-structured codebase (CI, tests, linting) | Experienced developer |
| Empty workspace, no LD presence | Treat as first-time |
1. `AskQuestion` — call it with a `prompt` and `options` array
2. `TaskAsk` or equivalent structured-input tool
3. (fallback) If the tool call fails or no such tool exists, THEN
render the question as numbered options in text and wait.
If experienced signals show, move faster: skip the orientation line, do each action and report it in one line, and jump to whichever step is incomplete. Either way the outcomes are the same: SDK installed, first flag evaluating, and MCP only if they ask for it.
Do NOT decide in advance whether you have the tool. Attempt the call. The tool call IS the question — do not also write the question as text.
---
Throughout this skill and its nested skills you will see decision-point markers formatted like this:
## Resume After Restart
```
**D1 -- BLOCKING:** <instruction to call your question tool>
- question: "<the question>"
- options:
- "<option A>" -> <what happens>
- "<option B>" -> <what happens>
- STOP. Do not continue until the user selects an option.
If the user says "continue onboarding," they are returning to the flow. Do not ask what was happening. Detect live state in order: check for a LaunchDarkly SDK package and init code, then for `variation()` calls, then for the `launchdarkly-onboarding` branch. Resume at the first incomplete step and say where things stand in one sentence ("SDK is installed. Creating your first flag now."). Then continue without preamble.
---
## Kickoff
When the user asks to set up LaunchDarkly:
1. Open directly. No "I'll help you" or "Let me start" filler. Two short sentences: a welcome, what onboarding does, and that nothing is committed without their approval. Example (adapt, do not copy verbatim):
> "Let's get you set up with LaunchDarkly. Once integrated, we'll create a test flag in your app so you can see how it works. No code will be changed without your approval."
2. Then start working. No roadmap table. One status line is fine ("Scanning your project now."), then go quiet.
3. Do not ask whether the user has an account. Infer it later: completing the SDK key step means they have one; if they cannot get a key, share the signup link at that point.
---
## Step 0: Safe Workspace
If this is a git repository, create and switch to a new branch named `launchdarkly-onboarding` before changing any files, so everything is isolated and reversible. If that branch already exists, append a short suffix. If this is not a git repo, skip silently.
Do not write an onboarding log or any summary file. Track state in memory for this session.
---
## Step 1: Explore
The scan runs in the background (see Experience Detection). Do not announce it beyond the one status line. Use its results when they land.
Classify the workspace before proceeding:
| State | Criteria | Action |
|---|---|---|
| **Clear app** | One language, a real entrypoint, one dependency manifest at the obvious location | Continue |
| **Unclear** | Minimal or conflicting signals, or a multi-package workspace (yarn/pnpm/npm workspaces, lerna, nx, turborepo, gradle, cargo, go) where more than one package could host LaunchDarkly | Ask (unclear form below) |
| **No app found** | No manifests, no entrypoints, empty workspace | Ask (no-app form below) |
A workspace with two or more candidate packages is always Unclear. Never guess which one to integrate.
**Ask form, unclear workspace** (one option per candidate package, with its path as the label):
```json
{
"questions": [
{
"id": "app_location",
"prompt": "I found multiple packages. Which one do you want to set up first?",
"options": [
{ "id": "candidate_1", "label": "<detected path, e.g. packages/api>" },
{ "id": "candidate_2", "label": "<detected path, e.g. packages/web>" },
{ "id": "demo", "label": "None of these. Scaffold a demo." },
{ "id": "other", "label": "Somewhere else. I'll tell you where." }
]
}
]
}
```
These are **instructions for you to follow**, not content to display. When you reach one: make the tool call (or render numbered options if no tool exists), then STOP and wait. Do NOT copy the marker text into your response.
**Ask form, no app found:**
```json
{
"questions": [
{
"id": "app_choice",
"prompt": "I didn't find a runnable app. How do you want to proceed?",
"options": [
{ "id": "demo_node", "label": "Scaffold a minimal Node.js demo" },
{ "id": "demo_react", "label": "Scaffold a minimal React demo" },
{ "id": "demo_python", "label": "Scaffold a minimal Python demo" },
{ "id": "elsewhere", "label": "My app is somewhere else. I'll point you to it." }
]
}
]
}
```
### User-Facing Communication
On a demo choice, scaffold a minimal app in a new subfolder (e.g. `launchdarkly-demo/`).
Every reply during onboarding must sound like a friendly, knowledgeable colleague walking someone through setup — not a workflow engine quoting internal instructions. Follow these rules in all user-facing output:
Identify language, framework, and environment type from dependency files (`package.json`, `go.mod`, `requirements.txt`/`pyproject.toml`, `pom.xml`/`build.gradle`, `Gemfile`, `*.csproj`, `Cargo.toml`). Search for existing LaunchDarkly usage (`launchdarkly`, `ldclient`, `LDClient`, `@launchdarkly`). Determine server-side, client-side, or mobile, which drives SDK selection. If LD is already integrated, note the SDK version so install can be skipped.
**Required response structure.** Every substantive onboarding reply must include:
Detect the coding agent for `--agent` flags: Cursor (`.cursor/`, `.cursorrules`), Claude Code (`~/.claude/`, `CLAUDE.md`), Windsurf (`.windsurfrules`), GitHub Copilot (`.github/copilot/`), Codex (`~/.codex/`, `AGENTS.md`). If ambiguous, ask.
1. **What we just did** — one or two sentences summarizing the completed action and its result.
2. **What we're doing next** — a plain-English preview of the next step.
3. **What you need to do** (only when the user has a manual action) — a concrete instruction, not a vague label like "Your turn." Include **where** to perform the action (e.g. "in Cursor's integrated terminal," "in the project folder," "in your browser," "in macOS Terminal").
---
**Forbidden in user-facing output:**
## Step 2: MCP (optional, after the flag)
- Internal decision-point IDs (D1, D5, D7, etc.), step numbers as labels (e.g. "Step 5 -- detect"), or skill file names (e.g. "sdk-install/apply/SKILL.md").
- Quoting or paraphrasing raw skill instructions, directive headings, or markdown from these files.
- Workflow-engine language ("BLOCKING," "STOP," "call your structured question tool," "proceed to the next nested skill").
Do not set up MCP on the way to the first flag, and do not ask about it during setup. Reach the first flag without it (Step 4 uses a dashboard link). Offer it only after the flag works, as one short choice:
**When telling the user to run a command**, always say **where** to run it. Good examples:
- "Run this in the integrated terminal in your editor"
- "Run this from the project root in your terminal"
- "Open a terminal in the `packages/api` folder and run …"
> "Want to manage flags from your editor next time? I can set that up. **[Set it up] [Skip]**"
Bad: "Run `npm install`" (without location context).
**Tone:** Friendly, conversational, and confident — like a knowledgeable colleague, not a manual. Use first person naturally (e.g. "I just detected that the flag was created, now I'm going to …"). Assume the reader is an engineer so don't over-explain basic concepts (what a package manager is, what an environment variable does), but do explain LaunchDarkly-specific concepts briefly on first mention (what a context is, what an SDK key is for, why there are different key types).
### Step Execution Rules
Do NOT treat the user's initial request (e.g. "onboard me," "set up LaunchDarkly") as blanket permission for file writes, installs, or configuration changes. Each action that modifies the repo, installs packages, or writes secrets requires its own consent at the step where it occurs.
**Blocking decision points** (you MUST halt and wait for the user's response before continuing):
| ID | Location | Question |
|----|----------|----------|
| D5-NOAPP | Step 5 -- detect | No runnable app found: user points to app or requests demo |
| D5-UNCLEAR | Step 5 -- detect | Weak evidence: user confirms the correct app folder |
| D5 | Step 5 -- detect | SDK confirmation / one-vs-both-SDKs scope choice |
| D7 | Step 5 -- apply | User chooses how secrets are set up: user-specified location, user handles it, or `.env` fallback. If user cannot provide keys, offer signup link. |
| D8 | Step 5 -- apply | Approval before changing non-LaunchDarkly dependencies |
| D9 | Step 6 | Auth errors (401/403): stop, do not retry automatically |
**Non-blocking** (you may proceed automatically): Steps 0-3 (log, explore, detect agent, install skills -- no user input needed), D6 plan preview (present and continue unless user objects), Step 5 detect (file reads only), compile check (Step 5 apply Step 4), follow-through file writes (`LAUNCHDARKLY.md`, editor rules).
## Core Principles
1. **Detect, don't guess:** Inspect the repo for language, framework, and package manager.
2. **Minimal changes:** Add SDK code alongside existing code; don't restructure the project.
3. **Match existing patterns:** Follow env vars, config files, and initialization patterns already in use.
4. **Validate end-to-end:** Confirm the SDK is connected before treating the first flag as proof of success.
5. **Paper trail:** Keep the Step 0 onboarding log current so another agent or session can continue without re-deriving context.
6. **Orient the user first:** On a fresh onboarding request, show the [Kickoff roadmap](#kickoff-onboarding-roadmap) before substantive work so the user knows the full arc.
7. **Defer credential questions:** Do not ask about account status or keys upfront. Account status is inferred through MCP OAuth (Step 4) or surfaced at D7 (Step 5) when keys are needed. Ask for **SDK keys / tokens** only in Step 4-5 when that step's skill says they are required ([Prerequisites](#prerequisites)).
8. **Deep-link to the dashboard:** When generating LaunchDarkly dashboard URLs and the **project key** and/or **environment key** are known (from MCP tools, user input, or the onboarding log), construct the most specific URL possible instead of linking to a generic page. Use these patterns:
| What you need to show | URL pattern |
|-----------------------|-------------|
| Project flags list | `https://app.launchdarkly.com/projects/{projectKey}/flags` |
| Specific flag | `https://app.launchdarkly.com/projects/{projectKey}/flags/{flagKey}` |
| Environment keys / SDK keys | `https://app.launchdarkly.com/projects/{projectKey}/settings/environments/{envKey}/keys` |
| Project environments list | `https://app.launchdarkly.com/projects/{projectKey}/settings/environments` |
| All projects | `https://app.launchdarkly.com/projects` |
Only generate deep links when the required keys are known from tool responses or confirmed user input. If they are unknown, use the most specific generic path available and tell the user how to navigate from there (e.g. "Open your project in the LaunchDarkly dashboard, then go to **Settings > Environments** to find your SDK key").
## Kickoff: onboarding roadmap
When the user invokes this onboarding flow (for example by asking you to follow this skill, run LaunchDarkly onboarding, or set up feature flags in the project), treat it as a **fresh kickoff** unless you are clearly resuming (see **Resuming** below).
### Kickoff sequence (new run — before any numbered step)
Perform these in **order** in the **same assistant turn**, then proceed directly into Steps 0-3:
1. **Task list:** Call your native task tool ([Progress Tracking](#progress-tracking)) and create **one task per step for Steps 0 through 6** (seven tasks minimum — one each for Steps 0, 1, 2, 3, 4, 5, and 6, even though Steps 0-3 are grouped as a single row below). Do this **before** rendering the roadmap so progress tracking is in place.
2. **Roadmap:** Give the user a brief, friendly preview of what you are about to do. Keep it conversational -- a short paragraph or a compact list is fine. Do not render a large table by default (the table below is your internal reference). The user should understand the arc (explore the project, set up tooling, install the SDK, create a first flag) without seeing step numbers or internal labels.
3. **Begin Steps 0-3 immediately.** These steps do not require a LaunchDarkly account or any user action. Run them in the background and surface only the results: what you found (language, framework, agent) and what you installed (companion skills). Do not narrate each step as a separate heading -- summarize them together when presenting findings to the user. Account status is inferred later (see [Prerequisites](#prerequisites)).
- **Resuming:** When the user says "continue LaunchDarkly onboarding" (or similar), **always check for `LAUNCHDARKLY_ONBOARDING.md` first**. If it exists:
1. Read the log to understand current state (completed steps, blockers, next step)
2. Show a brief "where we are" summary (e.g. "I see we finished MCP setup — next is SDK installation")
3. Refresh your task list to match the log's checklist
4. Continue from the log's **Next step** — do not restart from Step 0
| Step | What happens | You get |
|------|--------------|---------|
| **0-3** -- Setup | Create onboarding log, explore project, detect agent, install companion skills (`npx skills add` from `launchdarkly/ai-tooling`) | Stack summary, agent ID, `launchdarkly-flag-*` skills available |
| **4** -- MCP | Configure LaunchDarkly MCP; user enables server; agent probes for tools | MCP tools (or ldcli/API fallback); account confirmed via OAuth |
| **5** -- SDK install | detect -> plan -> apply ([sdk-install](sdk-install/SKILL.md)) | Packages + init wired to env vars |
| **6** -- First flag | Create boolean flag, evaluate, toggle, add interactive demo ([first-flag](first-flag/SKILL.md)) | End-to-end proof + visible "wow" moment |
| **Follow-through** | `LAUNCHDARKLY.md`, editor rules ([1.8-summary](references/1.8-summary.md), [1.9-editor-rules](references/1.9-editor-rules.md)) | Durable docs for the repo |
After presenting the roadmap preview, proceed directly into Steps 0-3 (they require no user input or account). Then continue with [Step 4](#step-4-configure-the-mcp-server).
## Workflow
Follow **Steps 0-6** in order unless an **Edge case** says otherwise. When **Step 6** (first flag) completes successfully, continue with [Default follow-through](#default-follow-through-not-numbered-steps).
### Steps 0-3: Setup (run silently -- do not narrate each step)
These four steps run automatically without user input. Perform them all, then present a single summary of what you found and what you set up. Do NOT show individual step headings, log-creation messages, or install output to the user.
**Step 0: Onboarding log.** Create or refresh `LAUNCHDARKLY_ONBOARDING.md` silently.
1. Look for an existing log at the repo root: `LAUNCHDARKLY_ONBOARDING.md`. If the project keeps docs under `docs/`, prefer `docs/LAUNCHDARKLY_ONBOARDING.md` when that folder already exists and the root file is absent.
2. Create or update the log file directly without asking for permission.
3. If resuming: read the log first, align with the stated **next step**, and only redo work the log marks incomplete or invalid.
4. What to write (update after each numbered step finishes or when something important changes):
- **Checklist:** Steps 0-6 with status (`not started` / `in progress` / `done` / `skipped` + brief reason).
- **Context:** coding agent id (once known), language/framework summary, monorepo target path if any, LaunchDarkly **project key** and **environment key** when known (never paste secrets or full SDK keys -- say "stored in env" or "user provided offline").
- **MCP:** configured yes/no, hosted vs fallback, link to config path if relevant.
- **Commands run:** e.g. `npx skills add ...` (no secrets).
- **Blockers / errors:** what failed and what was tried.
- **Next step:** single explicit step number and name (e.g. "Step 5: Install and Initialize the SDK").
5. After errors: append or edit the log with what broke and where you are resuming.
This file is a **working** log during onboarding. After success, it is deleted and replaced with `LAUNCHDARKLY.md` ([Onboarding Summary](references/1.8-summary.md)).
**Step 1: Explore the project.** Understand what you are integrating.
1. Identify language and framework. Check dependency files: `package.json`, `go.mod`, `requirements.txt` / `pyproject.toml` / `Pipfile`, `pom.xml` / `build.gradle`, `Gemfile`, `*.csproj` / `*.sln`, `Cargo.toml`, etc.
2. Check for existing LaunchDarkly usage. Search for `launchdarkly`, `ldclient`, `ld-client`, `LDClient`, `@launchdarkly`.
- If already present: note SDK version and patterns; you may shorten or skip [Step 5](#step-5-install-and-initialize-the-sdk) per edge cases.
- If not present: plan full SDK setup.
3. Identify environment type: server-side app, client SPA, mobile, edge, etc. -- this drives SDK choice.
Deep detection details: [Detect repository stack](sdk-install/detect/SKILL.md) (nested under [sdk-install](sdk-install/SKILL.md)).
**Step 2: Detect the agent environment.** Infer silently -- do not ask the user.
1. Check for indicators (in priority order — stop at the first strong match):
- **Cursor:** `.cursor/`, `.cursorrules`, or `CURSOR_` env vars
- **Claude Code:** `~/.claude/`, `CLAUDE.md`, or `CLAUDE_` env vars
- **Windsurf:** `.windsurfrules`
- **GitHub Copilot:** `.github/copilot/`
- **Codex:** `~/.codex/`, `AGENTS.md`
2. If multiple indicators are present, pick the one whose runtime you are **currently executing inside**. If none match, default to the agent whose tool surface you observe at runtime.
3. Remember the agent id for Step 3 (e.g. `cursor`, `claude-code`).
**Step 3: Install companion skills.** Install flag-management skills from the public repo so later steps can delegate when appropriate.
If they choose **Set it up**: follow [mcp-configure](mcp-configure/SKILL.md). When that nested skill is not available in the session, install it:
```bash
npx skills add launchdarkly/ai-tooling --skill launchdarkly-flag-create launchdarkly-flag-discovery launchdarkly-flag-targeting launchdarkly-flag-cleanup -y --agent <detected-agent>
npx skills add launchdarkly/ai-tooling --skill mcp-configure -y --agent <detected-agent>
```
Replace `<detected-agent>` with the value from Step 2. Confirm success; skip skills already installed.
If that fails, check `~/.agents/skills/` and `~/.cursor/skills/` for a cached copy, or configure the server inline using [MCP Config Templates](mcp-configure/references/mcp-config-templates.md). After it succeeds, call `get-project` once (`projectKey: "default"`) and store `projectKey` and `envKey` (`test`).
**Bundled vs public:** Orchestration and setup for this flow live **in this folder** -- parent [SKILL.md](SKILL.md), nested [mcp-configure](mcp-configure/SKILL.md), [sdk-install](sdk-install/SKILL.md) (detect / plan / apply), [first-flag](first-flag/SKILL.md), and `references/` ([SDK recipes](references/sdk/recipes.md), [snippets](references/sdk/snippets/), summary, editor rules, etc.). The command above installs **flag-management** skills from the public [launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling) repo only.
---
**After Steps 0-3 complete:** Present a single summary to the user covering what you found (language, framework, environment type, whether LD is already integrated, detected agent). Then proceed to [Step 4](#step-4-configure-the-mcp-server).
## Step 3: Install the SDK
### Step 4: Configure the MCP Server
Install the SDK and wire up initialization automatically. Do not ask how, and do not explain what the SDK is. Tell the user one line: "Scan complete. Installing SDK." Then proceed.
Hand off to [mcp-configure/SKILL.md](mcp-configure/SKILL.md) for setup (hosted MCP, quick install, manual JSON, agent authorization).
Hand off to [sdk-install](sdk-install/SKILL.md) with the stack context from Step 1. It runs detect, plan, and apply: selects the package, installs it, and wires initialization to match the codebase. When that nested skill is not available in the session, install it:
MCP setup requires the user to act outside the agent (clicking a quick-install link, completing OAuth, enabling the server in editor settings). After presenting the instructions, **tell the user to enable the server and complete OAuth**. Then probe for MCP tools immediately — a restart may not be required in Cursor or Claude Code.
```bash
npx skills add launchdarkly/ai-tooling --skill sdk-install -y --agent <detected-agent>
```
**Auto-verify:** After the user confirms they've enabled the server, probe for MCP by calling a lightweight MCP tool such as `list-feature-flags` with the known project key. If the tool responds normally, MCP is live — note it in the onboarding log and use MCP tools for later steps. If the call fails or no MCP tools are visible, **update the onboarding log first** (so a new session can resume), then suggest a restart with clear instructions: tell the user to say **"continue LaunchDarkly onboarding"** when they come back. If restart doesn't help, fall back to ldcli/API for Steps 5-6 and note the fallback in the onboarding log. **Do not ask** the user whether MCP is working — find out by trying it.
When the app was scaffolded by the agent in Step 1, skip the nested skill and use the fast path directly (the stack is known; skip `npm run build`):
Do not duplicate MCP procedures in this file. Do not block Step 5 indefinitely on MCP.
| Scaffold | Package | Install | Env var | Entrypoint | Init |
|---|---|---|---|---|---|
| React (Vite) | `launchdarkly-react-client-sdk` | `npm install launchdarkly-react-client-sdk` | `VITE_LAUNCHDARKLY_CLIENT_SIDE_ID` | `src/main.jsx`/`.tsx` | `asyncWithLDProvider` around the root render |
| Node.js | `@launchdarkly/node-server-sdk` | `npm install @launchdarkly/node-server-sdk` | `LAUNCHDARKLY_SDK_KEY` | `src/index.js`/`server.js` | `init(sdkKey)` then `waitForInitialization()` |
| Python | `launchdarkly-server-sdk` | `pip install launchdarkly-server-sdk` | `LAUNCHDARKLY_SDK_KEY` | `app.py`/`main.py` | `ldclient.set_config(Config(sdk_key))` then `ldclient.get()` |
### Step 5: Install and Initialize the SDK
Rules: the SDK key lives in an environment variable, never hardcoded. One client instance, shared. Wait for initialization before evaluating flags.
If the project **already has LaunchDarkly installed and initialized** (see [detect decision tree](sdk-install/detect/SKILL.md#decision-tree)), skip to [Step 6: Create Your First Feature Flag](#step-6-create-your-first-feature-flag).
### SDK key
Otherwise hand off to [LaunchDarkly SDK Install (onboarding)](sdk-install/SKILL.md), which runs nested skills in order: [Detect repository stack](sdk-install/detect/SKILL.md) -> [Generate integration plan](sdk-install/plan/SKILL.md) -> [Apply code changes](sdk-install/apply/SKILL.md), using [SDK recipes](references/sdk/recipes.md) and [SDK snippets](references/sdk/snippets/). If the user asked for **both** server and client (e.g. API + SPA, Next.js server + browser), follow [Dual SDK integrations](sdk-install/plan/SKILL.md#dual-sdk-integrations) through plan and apply so **both** SDKs are really installed and initialized.
The SDK needs a key. Default to fetching it for the user when MCP is connected; otherwise give them the direct link and let them paste it. Ask only if you cannot determine the path:
**Blocking decision points inside Step 5** (see nested skills): D5 (SDK scope), D7 (secret consent), D8 (dependency changes). Do NOT batch tool calls across these boundaries. D6 (plan preview) is non-blocking -- present the plan and continue unless the user objects.
```json
{
"questions": [
{
"id": "sdk_key_setup",
"prompt": "Do you have a LaunchDarkly account?",
"options": [
{ "id": "yes", "label": "Yes" },
{ "id": "no_account", "label": "Not yet" }
]
}
]
}
```
### Step 6: Create Your First Feature Flag
- Account, MCP connected: fetch the key via `get-environments`, write it to `.env`, and ensure `.env` is gitignored. Never print key values.
- Account, no MCP: give the direct link and have them paste it. `https://app.launchdarkly.com/projects/{projectKey}/settings/environments/{envKey}/keys`
- No account: share the resolved signup link. Write placeholder env vars so the code compiles, and continue.
Create and evaluate a boolean flag; toggle and observe end-to-end.
Key type must match the integration: server-side SDK takes an **SDK key**, browser/client-side takes a **client-side ID**, mobile takes a **mobile key**. Env variable names and bundler rules live in [Apply code changes](sdk-install/apply/SKILL.md).
1. Follow [Create first feature flag](first-flag/SKILL.md).
2. If the **`launchdarkly-flag-create`** skill (installed in Step 3) is available, you may use it for create/evaluation wiring **only** while still completing the verify/toggle checklist in [Create first feature flag](first-flag/SKILL.md). Onboarding must remain completable without it.
Do not proceed until initialization is verified.
Install or refresh flag skills via:
---
`npx skills add launchdarkly/ai-tooling --skill launchdarkly-flag-create -y --agent <detected-agent>`
## Step 4: First Flag
See D9 in [first-flag](first-flag/SKILL.md) for the blocking stop on auth errors.
Create the flag, wire it into the app, and let the user watch it turn on.
## Default follow-through (not numbered steps)
- **Create the flag.** If MCP is connected, call `create-flag` (on a duplicate-key conflict, call `get-flag` and adopt the existing flag; do not `list-flags` first). If MCP is not connected, give them a dashboard link that opens the create form with the key prefilled and have them create it: `https://app.launchdarkly.com/projects/{projectKey}/flags/new?key={flagKey}`
- **Add a flag-gated banner.** Insert a small, clean banner at the top of the app's main page, gated on the flag. Off state: a neutral banner reading "LaunchDarkly test banner (flag is off)" with a link to view the flag in LaunchDarkly. On state: the banner switches to a clearly different look (for example a green background) reading "LaunchDarkly test banner (flag is on)". Style it so it looks intentional, not like debug output. Add to the existing app, do not rewrite it. For an app with no rendered page, add one equivalent visible output (an endpoint or a startup line) that changes with the flag.
- **Start the dev server on a free port** (check `lsof -ti :3000,4000,5173` first). **Keep it running until the user has seen the flag turn on. Do not stop the server before then.**
- **Hand off the reveal to the user.** Give them the local URL and one choice for turning it on:
Do these when finishing onboarding -- same session when possible. They are **documentation and handoff** tasks, not repeats of Steps 0-6. **Do not skip this section** -- it is the primary deliverable the user keeps after onboarding.
```json
{
"questions": [
{
"id": "flip_method",
"prompt": "Your app is running at <url> and the flagged element is off. Turn the flag on to watch it change. How do you want to flip it?",
"options": [
{ "id": "ld_ui", "label": "I'll flip it in LaunchDarkly" },
{ "id": "agent", "label": "Flip it for me" }
]
}
]
}
```
**Setup summary (`LAUNCHDARKLY.md`) -- REQUIRED**
- If **I'll flip it in LaunchDarkly**: give them the direct link to the flag and wait. `https://app.launchdarkly.com/projects/{projectKey}/flags/{flagKey}/targeting?env={envKey}`
- If **Flip it for me**: turn it on via `toggle-flag` (MCP) or the REST API, whichever is configured. If neither is, fall back to the dashboard link.
- Only offer **Flip it for me** when MCP or an API token is actually configured. Otherwise show just the LaunchDarkly option.
Generate the repo summary per [Onboarding Summary](references/1.8-summary.md). Write it directly -- this is part of the onboarding workflow. The generated `LAUNCHDARKLY.md` **must** include all of the following (see template in that reference):
Do not print the page or the banner text in chat. Point the user to their browser: the banner at `<url>` flips live with the server still running. That is the flag working.
1. **SDK Details** -- which SDK(s) are installed, package names, key types, initialization files
2. **Configuration** -- env var names, how secrets are managed, bundler-specific conventions
3. **Where to Find Things** -- dashboard links with real project key substituted
4. **How Feature Flags Work** -- a language-specific code example showing flag evaluation in this project's stack (not a generic snippet -- use the same pattern the agent wired during Step 5)
5. **Next Steps / Advanced Capabilities** -- links to Percentage Rollouts, Targeting Rules, Experimentation, configs, Guarded Rollouts, and Observability
6. **Agent Integration** -- MCP server setup for continued agent-driven flag management
### Wrap-up
This is **not** the same file as `LAUNCHDARKLY_ONBOARDING.md`. The onboarding log is a working checklist; `LAUNCHDARKLY.md` is the **permanent reference** for the team.
Keep it to a few lines:
- The flag is live. See it in LaunchDarkly: `https://app.launchdarkly.com/projects/{projectKey}/flags/{flagKey}/targeting?env={envKey}`
- Nothing is committed. Your changes are on the `launchdarkly-onboarding` branch, so you can review, keep, or drop them however you like.
- One choice for what's next:
**Clean up the onboarding log:** After writing `LAUNCHDARKLY.md`, **delete** `LAUNCHDARKLY_ONBOARDING.md` (or `docs/LAUNCHDARKLY_ONBOARDING.md` if that was the location used). This is part of the workflow -- do not ask for permission. Removing the working log avoids confusion from having two LaunchDarkly docs in the repo.
```json
{
"questions": [
{
"id": "explore_next",
"prompt": "Want to explore more of LaunchDarkly?",
"options": [
{ "id": "experimentation", "label": "Experimentation: test changes and measure impact" },
{ "id": "observability", "label": "Observability: monitor flags and errors in production" },
{ "id": "ai_configs", "label": "AI Configs: manage AI models and prompts" },
{ "id": "done", "label": "Not now" }
]
}
]
}
```
**Editor rules / skills**
---
- Add editor-specific rules or skill hooks per [Editor Rules and Skills](references/1.9-editor-rules.md). Write them directly -- this is part of the onboarding workflow.
## Redirecting Drift
If the user asks to skip a step or jump ahead mid-flow, your first reply always does three things, in order, before writing code or skipping:
1. Acknowledge what they asked for, in their words.
2. Name the concrete consequence of skipping in one sentence. The specific thing that breaks (e.g. "the flag calls won't run until the SDK is installed"). State the real failure, not a vague allusion.
3. Offer the choice: finish the quick step first, or proceed their way.
> "I hear you, you want the flag code now. Without the SDK installed those calls won't run. Setup takes about two minutes. Want me to finish that first, or hand you the code to wire up after?"
Never silently dump code with no tradeoff, and never rigidly refuse. If they insist, respect it, note what was skipped, restate the risk in one sentence, and keep moving.
---
## Skill Repositories
| Repo | Skills | Purpose |
|------|--------|---------|
| `launchdarkly/ai-tooling` | `onboarding`, `sdk-install`, `mcp-configure` | Setup |
| `launchdarkly/ai-tooling` | `launchdarkly-flag-create` and related | Flag management |
---
## Edge Cases
| Situation | Action |
|-----------|--------|
| SDK already installed **and** initialized (see [detect decision tree](sdk-install/detect/SKILL.md#decision-tree)) | Skip **Step 5**; go to **Step 6** (First flag) |
| SDK in dependencies **but** not initialized | Continue **Step 5** from [apply](sdk-install/apply/SKILL.md) / init (see [sdk-install](sdk-install/SKILL.md)); do not skip validation |
| SDK state unclear | Re-run [Detect repository stack](sdk-install/detect/SKILL.md), then follow its decision tree |
| No runnable app found or app target unclear | Follow the workspace classification in [Detect: classify workspace confidence](sdk-install/detect/SKILL.md#5a-classify-workspace-confidence) — ask the user to point to the real app or offer to create a demo. Do not proceed to plan or apply without a confirmed app target. |
| Multiple languages in repo | **Blocking (D5):** use question tool to ask which target to integrate first -- do not guess |
| User wants **both** frontend and backend (or server + browser) in the same target | [Dual SDK plan](sdk-install/plan/SKILL.md#dual-sdk-integrations): two packages, two entrypoints, two inits; [apply](sdk-install/apply/SKILL.md) must complete **both** tracks |
| Monorepo | **Blocking (D5):** use question tool to ask which package/service to integrate -- do not assume the root |
| No package manager detected | **Blocking (D5):** use question tool to ask which SDK to install; provide manual install instructions from [SDK recipes](references/sdk/recipes.md) |
| Companion flag skills already installed (Step 3) | Skip re-running `npx skills add` for those skill names |
| Resuming after a break or new agent session | Read `LAUNCHDARKLY_ONBOARDING.md` (Step 0); continue from **Next step**; refresh the log as you go |
| MCP configuration fails or user declines MCP | Continue with **Step 5** using ldcli/API/dashboard per [mcp-configure](mcp-configure/SKILL.md); note limitation for flag tooling |
| User / repo already fully onboarded | Summarize state from Step 0 log and repo; offer next steps without redoing completed steps |
## What NOT to Do
- Don't install an SDK without exploring the project and detecting the stack (Steps 1 and 5); keep the Step 0 log updated as you go.
- Don't upgrade, pin, or add **non-LaunchDarkly** dependencies (peer-deps, lockfile churn, "latest" bumps) to install or compile the SDK without **explicit user approval** -- see [Apply -- Permission before changing other dependencies](sdk-install/apply/SKILL.md#permission-before-changing-other-dependencies).
- Don't hardcode SDK keys in source code -- always use environment variables (see [Apply code changes](sdk-install/apply/SKILL.md)).
- Don't restructure the user's project or refactor unrelated code.
- Don't create flags before **Step 5** (SDK install) completes.
- Don't write decision-point questions as chat text -- use your structured question tool (see [Decision Points](#decision-points)).
- **SDK already installed:** Skip Step 3. Say so in one line that names what you found and where, then go to Step 4. Do not re-explain the SDK or run install commands.
- **MCP already configured:** Use it. Skip the Step 2 offer. Call `get-project` to store keys and continue.
- **Deprecated mcp/aiconfigs or mcp/fm found:** Both are deprecated. Ask before migrating to the unified `mcp/launchdarkly` server. Do not auto-migrate.
- **No supported agent detected:** Ask directly. Provide manual config if needed.
- **npx not available:** Provide manual skill installation (clone repo, copy skill directories).
- **User only wants partial setup:** Respect it. State what is missing and what that limits.
- **Non-LaunchDarkly dependencies would have to change** (peer-dep bumps, lockfile churn) to install or compile the SDK: get explicit approval first, per [Apply code changes](sdk-install/apply/SKILL.md).
## References
**Continuity**
- Step 0 -- `LAUNCHDARKLY_ONBOARDING.md` (working log; see [Steps 0-3](#steps-0-3-setup-run-silently----do-not-narrate-each-step))
**Step 4 -- MCP (nested skill is primary)**
- [mcp-configure/SKILL.md](mcp-configure/SKILL.md) -- hosted MCP, verify, edge cases (**follow this first**)
- [MCP UI links](mcp-configure/references/mcp-ui-links.md) -- HTTPS + `command:` links to open MCP settings per editor
- [MCP Config Templates](mcp-configure/references/mcp-config-templates.md) -- per-agent JSON for hosted MCP
**Step 5 -- SDK install (nested skills)**
- [sdk-install/SKILL.md](sdk-install/SKILL.md) -- orchestrates **detect -> plan -> apply** (**follow this first**)
- [Detect repository stack](sdk-install/detect/SKILL.md)
- [Generate integration plan](sdk-install/plan/SKILL.md)
- [Apply code changes](sdk-install/apply/SKILL.md)
**First flag (Step 6)**
- [Create first feature flag](first-flag/SKILL.md)
**Default follow-through**
- [Onboarding Summary](references/1.8-summary.md) -- template for `LAUNCHDARKLY.md`
- [Editor Rules and Skills](references/1.9-editor-rules.md)
**SDK index**
- [SDK recipes](references/sdk/recipes.md)
- [SDK snippets](references/sdk/snippets/)
**Public flag skills (install via Step 3)**
- [github.com/launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling) -- `launchdarkly-flag-create`, `launchdarkly-flag-discovery`, `launchdarkly-flag-targeting`, `launchdarkly-flag-cleanup`
- [mcp-configure](mcp-configure/SKILL.md) and [MCP Config Templates](mcp-configure/references/mcp-config-templates.md) — Step 2
- [sdk-install](sdk-install/SKILL.md) — Step 3 (detect, plan, apply)
- [SDK recipes](references/sdk/recipes.md) and [SDK snippets](references/sdk/snippets/) — per-SDK install and init detail
-382
View File
@@ -1,382 +0,0 @@
---
name: first-flag
description: "Create a boolean first flag, add evaluation, toggle on/off for end-to-end proof. Parent onboarding Step 6; uses MCP, API, or ldcli; optional flag-create skill."
license: Apache-2.0
compatibility: Requires SDK installed (parent Step 5) and LaunchDarkly project access
metadata:
author: launchdarkly
version: "0.1.0"
---
# Create first feature flag
The SDK is connected. Now help the user create their first feature flag and see it work end-to-end.
This skill is nested under [LaunchDarkly onboarding](../SKILL.md); the parent **Step 6** is **first flag**. **Prior:** [Apply code changes](../sdk-install/apply/SKILL.md).
**Optional -- Flag Create skill already installed:** If the **`launchdarkly-flag-create`** skill from [github.com/launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling) is available in the session (install with `npx skills add launchdarkly/ai-tooling --skill launchdarkly-flag-create -y --agent <agent>`), you may use it for **creating the flag** and **choosing evaluation code** that matches the repo. You must still complete **default off -> verify OFF -> toggle on -> verify ON** (Steps 3-5 below). **Do not** require that skill: this page stays the full fallback when it is missing or MCP-only flows conflict with the user's setup.
## Security: Credential handling
**Never substitute literal token values into commands.** Use environment variable references instead:
- Shell commands: `$LAUNCHDARKLY_ACCESS_TOKEN` (expanded by the shell, not visible in `ps` output)
- Set the variable in your session: `export LAUNCHDARKLY_ACCESS_TOKEN=<your-token>`
This prevents tokens from appearing in process lists, shell history, and screen recordings.
## Step 0: Consult SDK flag-key guidance
Before creating the flag or wiring evaluation code, check the [Flag key behavior by SDK](#flag-key-behavior-by-sdk) table below. Some SDKs transform flag keys before exposing them in application code (e.g. the React SDK camelCases kebab-case keys). The flag key you create in LaunchDarkly, the SDK/framework configuration, and the key you reference in code must all align.
- **If the SDK transforms keys** (e.g. React `useFlags()` camelCases `my-first-flag``myFirstFlag`): generate evaluation code using the **transformed** key. The flag key in LaunchDarkly stays as-is (kebab-case is conventional).
- **If the SDK preserves keys as-is** (most server-side SDKs): use the exact LaunchDarkly flag key string in code.
- **If the SDK supports both modes** (e.g. React allows disabling camelCase via provider options): decide which mode the project uses (check existing code or provider config), then generate code that matches.
### Flag key behavior by SDK
| SDK | Key transformation | Code key for `my-first-flag` | Notes |
|-----|--------------------|------------------------------|-------|
| React Web (`useFlags()`) | camelCase by default | `myFirstFlag` | `reactOptions: { useCamelCaseFlagKeys: false }` on the provider disables this |
| React Native (`useFlags()`) | camelCase by default | `myFirstFlag` | Same `reactOptions` override available |
| Vue (`useLDFlag()`) | None (pass original key) | `'my-first-flag'` | |
| JavaScript Browser | None | `'my-first-flag'` | |
| Node.js Server | None | `'my-first-flag'` | |
| Python Server | None | `'my-first-flag'` | |
| Go Server | None | `"my-first-flag"` | |
| Java Server | None | `"my-first-flag"` | |
| .NET Server | None | `"my-first-flag"` | |
| Ruby Server | None | `'my-first-flag'` | |
| Swift/iOS | None | `"my-first-flag"` | |
| Android | None | `"my-first-flag"` | |
| Flutter | None | `'my-first-flag'` | |
When wiring the evaluation code in Step 2 below, use the **Code key** column value, not the raw LaunchDarkly key, whenever the SDK applies a transformation.
## Step 1: Create the flag
**REST / curl auth:** Use `$LAUNCHDARKLY_ACCESS_TOKEN` as the `Authorization` header value (LaunchDarkly uses the raw token, no `Bearer` prefix). The shell expands the variable but doesn't log it.
### Via MCP (preferred)
If the LaunchDarkly MCP server is available, use `create-feature-flag` (or the equivalent flag-creation tool your server exposes):
- **Key**: `my-first-flag` (or a name relevant to the user's project)
- **Name**: "My First Flag"
- **Kind**: `boolean`
- **Variations**: `true` / `false`
- **Temporary**: `true`
### Via LaunchDarkly API
```bash
curl -s -X POST \
"https://app.launchdarkly.com/api/v2/flags/PROJECT_KEY" \
-H "Authorization: $LAUNCHDARKLY_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My First Flag",
"key": "my-first-flag",
"kind": "boolean",
"variations": [
{"value": true},
{"value": false}
],
"temporary": true
}'
```
### Via ldcli
```bash
ldcli flags create \
--access-token "$LAUNCHDARKLY_ACCESS_TOKEN" \
--project PROJECT_KEY \
--data '{"name": "My First Flag", "key": "my-first-flag", "kind": "boolean", "temporary": true}'
```
After creation, the flag starts with **targeting OFF**, serving the off variation (`false`) to everyone. When the project key is known, link the user to the flag's dashboard page: **`https://app.launchdarkly.com/projects/{projectKey}/flags/my-first-flag`** (substitute the real project key).
## Step 2: Add flag evaluation code
Add code to evaluate the flag in the application. Place this where it makes sense for the user's feature.
### Server-side examples
```javascript
// Node.js (@launchdarkly/node-server-sdk) -- ldClient is your initialized server client after waitForInitialization
const context = { kind: 'user', key: 'example-user-key', name: 'Example User' };
const showFeature = await ldClient.boolVariation('my-first-flag', context, false);
if (showFeature) {
console.log('Feature is ON');
} else {
console.log('Feature is OFF');
}
```
```python
# Python (launchdarkly-server-sdk) -- client is ldclient.get() after set_config
from ldclient import Context
context = Context.builder("example-user-key").name("Example User").build()
show_feature = client.variation("my-first-flag", context, False)
if show_feature:
print("Feature is ON")
else:
print("Feature is OFF")
```
```go
// Go
context := ldcontext.NewBuilder("example-user-key").Name("Example User").Build()
showFeature, _ := ldClient.BoolVariation("my-first-flag", context, false)
if showFeature {
fmt.Println("Feature is ON")
} else {
fmt.Println("Feature is OFF")
}
```
### Client-side examples
```tsx
// React — useFlags() camelCases keys: "my-first-flag" → myFirstFlag (see Step 0 table)
import { useFlags } from 'launchdarkly-react-client-sdk';
function MyComponent() {
const { myFirstFlag } = useFlags();
return (
<div>
{myFirstFlag ? <p>Feature is ON</p> : <p>Feature is OFF</p>}
</div>
);
}
```
The React SDK's `useFlags()` hook camelCases kebab-case flag keys by default, so `my-first-flag` becomes `myFirstFlag`. If the project disables this via `reactOptions: { useCamelCaseFlagKeys: false }` on the provider, use the original key string instead. Always check the project's provider configuration before choosing which form to use — see the [Flag key behavior table](#flag-key-behavior-by-sdk) above.
## Step 3: Verify the default value
With targeting OFF, the flag should evaluate to `false`. Run the application and confirm:
```
Feature is OFF
```
## Step 4: Toggle the flag on
### Via MCP
The LaunchDarkly MCP server exposes **`update-feature-flag`** (JSON Patch), not a tool named `toggle-flag` -- use the tool names your MCP server lists.
**Simplest path:** Prefer **ldcli** or the **LaunchDarkly API** block below when you only need to turn the flag on once.
**If using `update-feature-flag`:** Call it with `projectKey`, `featureFlagKey`, and `PatchWithComment.patch` as a JSON Patch array. Turning the flag **on** for an environment typically uses a `replace` operation on that environment's `on` field (confirm the exact path from `get-feature-flag` for your account if needed):
```json
{
"projectKey": "PROJECT_KEY",
"featureFlagKey": "my-first-flag",
"PatchWithComment": {
"patch": [
{
"op": "replace",
"path": "/environments/ENVIRONMENT_KEY/on",
"value": true
}
],
"comment": "Onboarding: turn on my-first-flag"
}
}
```
Replace `ENVIRONMENT_KEY` with the environment key for the environment you are targeting (e.g. `test`, `production`).
### Via LaunchDarkly API
```bash
curl -s -X PATCH \
"https://app.launchdarkly.com/api/v2/flags/PROJECT_KEY/my-first-flag" \
-H "Authorization: $LAUNCHDARKLY_ACCESS_TOKEN" \
-H "Content-Type: application/json; domain-model=launchdarkly.semanticpatch" \
-d '{
"environmentKey": "ENVIRONMENT_KEY",
"instructions": [
{"kind": "turnFlagOn"}
]
}'
```
### Via ldcli
```bash
ldcli flags toggle-on \
--access-token "$LAUNCHDARKLY_ACCESS_TOKEN" \
--project PROJECT_KEY \
--environment ENVIRONMENT_KEY \
--flag my-first-flag
```
## Step 5: Verify the toggle
After toggling the flag on, the application should now show:
```
Feature is ON
```
For server-side SDKs using streaming (the default), the change should be reflected within seconds. For client-side SDKs, the change appears on the next page load or when the SDK polls for updates.
## Step 6: Add an interactive demo
Now that the flag works, add a **visible, interactive element** so the user can see the flag in action -- not just a console log. This creates a "wow" moment and gives the user a tangible proof point they can show others.
**Choose the right demo based on what you detected:**
| App type | What to add | User experience |
|----------|-------------|-----------------|
| **Frontend (React, Vue, SPA)** | A banner, badge, or button gated by the flag | Toggle flag in dashboard → refresh page → element appears/disappears |
| **Backend API (Node, Python, Go, etc.)** | A `/launchdarkly-demo` endpoint that returns flag state as JSON | `curl` the endpoint → toggle flag → `curl` again → response changes |
| **Full-stack (Next.js SSR, Rails, etc.)** | Both: an API endpoint + a UI element that displays the flag state | Toggle flag → see both API and UI reflect the change |
| **CLI / script** | A `--feature-demo` flag or distinct output mode | Run script → toggle flag → run again → output changes |
### Frontend demo example (React)
Add a component or element that's visually obvious when the flag is on:
```tsx
// Add to an existing page component
import { useFlags } from 'launchdarkly-react-client-sdk';
function FeatureFlagDemo() {
const { myFirstFlag } = useFlags();
if (!myFirstFlag) return null;
return (
<div style={{
padding: '12px 20px',
backgroundColor: '#405BFF',
color: 'white',
borderRadius: '8px',
margin: '16px 0',
fontWeight: 500
}}>
LaunchDarkly is working this banner is controlled by a feature flag
</div>
);
}
```
Place it somewhere visible (e.g., at the top of the main page or in a dashboard/header area).
### Backend demo example (Node.js/Express)
Add an endpoint that returns the flag state:
```javascript
// Add to your Express app (or equivalent for other frameworks)
app.get('/launchdarkly-demo', async (req, res) => {
const context = { kind: 'user', key: 'demo-user' };
const flagValue = await ldClient.boolVariation('my-first-flag', context, false);
res.json({
flag: 'my-first-flag',
enabled: flagValue,
message: flagValue
? 'LaunchDarkly is working — the flag is ON'
: 'LaunchDarkly is working — the flag is OFF'
});
});
```
Tell the user to test with: `curl http://localhost:PORT/launchdarkly-demo`
### Backend demo example (Python/Flask)
```python
@app.route('/launchdarkly-demo')
def launchdarkly_demo():
context = Context.builder("demo-user").build()
flag_value = ld_client.variation("my-first-flag", context, False)
return jsonify({
"flag": "my-first-flag",
"enabled": flag_value,
"message": "LaunchDarkly is working — the flag is ON" if flag_value
else "LaunchDarkly is working — the flag is OFF"
})
```
### Full-stack demo
For apps with both server and client (e.g., Next.js, Remix, Rails with frontend):
1. Add the API endpoint (backend example above)
2. Add a UI component that either calls the endpoint or uses the client SDK directly
3. The user can verify both paths work
### Guidelines
1. **Match existing patterns** -- use the same routing style, component conventions, and code style as the rest of the app
2. **Make it obvious** -- use color, position, or text that clearly indicates this is the LaunchDarkly demo
3. **Keep it removable** -- add a brief comment like `// LaunchDarkly demo - safe to remove` so the user knows they can delete it later (or keep it as a template)
4. **Don't over-engineer** -- this is a demo, not a production feature; a few lines of code is ideal
### Walk the user through it
After adding the demo element:
1. Tell the user where you added it and how to see it (URL, page location, command)
2. Have them verify it shows the current flag state
3. Ask them to toggle the flag in the dashboard (provide the deep link: `https://app.launchdarkly.com/projects/{projectKey}/flags/my-first-flag`)
4. Have them refresh/re-run to see the change
5. Celebrate the successful integration
## Congratulations
The user has successfully:
1. Installed the LaunchDarkly SDK
2. Connected it to LaunchDarkly
3. Created a feature flag
4. Evaluated it in code
5. Toggled it and seen the result
6. Added an interactive demo they can show others
This is the "proof point" moment -- the user has a working feature flag they can toggle in real-time. The demo element makes it tangible and shareable.
**Encourage the next skill:** Suggest they **install or enable** the **`launchdarkly-flag-create`** skill from [github.com/launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling) (`npx skills add launchdarkly/ai-tooling --skill launchdarkly-flag-create -y --agent <agent>`) so future work -- creating flags that match repo conventions, wrapping features, and verifying wiring -- has a dedicated playbook. Offer to help them add it if they are unsure how.
## Error handling
### Authorization errors
If any step returns a **401** or **403**:
**D9 -- BLOCKING:** Call your structured question tool now.
- question: "I received an authorization error ([specific status code and message]). This requires your action to resolve -- I cannot retry automatically."
- options:
- "I'll re-authenticate -- run ldcli login or refresh my token"
- "Let me check my access token and try again"
- "I don't have an account -- help me sign up"
- "The project or environment doesn't exist -- help me create one"
- STOP. Do not write the question as text. Do not retry authorization errors automatically -- they always require user action. Do not continue until the user selects an option.
### Other errors
For non-auth errors (flag creation failures, SDK key mismatches, flags returning fallback values, etc.), diagnose the issue using the error output, application logs, and your understanding of the project.
**Next steps to suggest:**
- **Install** **`launchdarkly-flag-create`** from [github.com/launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling) if it is not already available -- this onboarding flow only covers a first boolean flag; that skill guides real-world flag creation aligned with existing code patterns (requires LaunchDarkly MCP per that skill's prerequisites).
- Use **`launchdarkly-flag-targeting`** from the same distribution to set up percentage rollouts and targeting rules
- Read the [LaunchDarkly docs](https://docs.launchdarkly.com) for advanced topics like contexts, experimentation, and metrics
---
**Upon completion, continue with:** [Onboarding summary](../references/1.8-summary.md) and [Editor rules and skills](../references/1.9-editor-rules.md) (default follow-through in the parent onboarding skill -- **not** MCP setup, which is Step 4). For MCP install or troubleshooting, use [mcp-configure](../mcp-configure/SKILL.md) and [MCP Config Templates](../mcp-configure/references/mcp-config-templates.md).
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "onboarding",
"description": "Onboard a project to LaunchDarkly: kickoff roadmap, resumable log, explore repo, MCP, companion flag skills, nested SDK install (detect/plan/apply), first flag.",
"description": "Onboard a project to LaunchDarkly: quiet scripted flow, explore repo, nested SDK install (detect/plan/apply), first flag with a live reveal, MCP offered afterwards.",
"version": "0.1.0",
"author": "LaunchDarkly",
"repository": "https://github.com/launchdarkly/ai-tooling",
+66 -34
View File
@@ -1,6 +1,6 @@
---
name: mcp-configure
description: "Configure the LaunchDarkly hosted MCP server during onboarding. Use when the parent LaunchDarkly onboarding skill reaches Step 4 (MCP). Supports Cursor, Claude Code, Windsurf, GitHub Copilot, and other MCP-compatible agents. OAuth authentication; no API keys for the hosted server."
description: "Configure the LaunchDarkly hosted MCP server during onboarding. Use when the parent LaunchDarkly onboarding skill reaches the MCP offer, after the first flag works. Supports Cursor, Claude Code, Windsurf, GitHub Copilot, and other MCP-compatible agents. OAuth authentication; no API keys for the hosted server."
license: Apache-2.0
compatibility: Requires an MCP-compatible coding agent and a LaunchDarkly account
metadata:
@@ -12,20 +12,24 @@ metadata:
Configures the LaunchDarkly hosted MCP server so flag management skills and onboarding can use MCP tools. Uses OAuth for authentication — no API keys needed for the hosted server.
This skill is nested under [LaunchDarkly onboarding](../SKILL.md); the parent skill's **Step 4** hands off here. **Hosted MCP** is the default and the only supported option for this onboarding flow.
This skill is nested under [LaunchDarkly onboarding](../SKILL.md); the parent skill hands off here once the first flag works.
## Prerequisites
- A LaunchDarkly account (sign up at the resolved signup URL — see [Source Attribution](../SKILL.md#source-attribution) in the parent skill; default: `https://app.launchdarkly.com/signup?source=agent`)
- A LaunchDarkly account (when directing users to sign up, use the resolved signup URL from the parent skill's [Source Attribution](../SKILL.md#source-attribution); default: `https://app.launchdarkly.com/signup?source=agent`)
- An MCP-compatible coding agent
## Hosted MCP Server
## Hosted MCP Servers
LaunchDarkly provides a unified hosted MCP server that handles feature management, AgentControl, and other LaunchDarkly capabilities.
LaunchDarkly provides a unified hosted MCP server for all functionality:
| Server | URL | Purpose |
| ----------- | ------------------------------------------------ | -------------------------------------------- |
| LaunchDarkly | `https://mcp.launchdarkly.com/mcp/launchdarkly` | Feature flags, AgentControl, and more |
| Server | URL | Purpose |
| ----------- | ----------------------------------------------- | ----------------------------- |
| LaunchDarkly (unified) | `https://mcp.launchdarkly.com/mcp/launchdarkly` | Feature flags and AgentControl |
The legacy `mcp/fm` and `mcp/aiconfigs` URLs are **deprecated** — migrate any existing usage to the unified server (see [Edge Cases](#edge-cases)).
For onboarding, the unified server is all that's needed.
## Workflow
@@ -58,7 +62,7 @@ Locate the MCP config file for the detected agent and add the hosted server entr
| GitHub Copilot | Repo **Settings** on GitHub.com → Copilot → Cloud agent → MCP (see [MCP UI links](references/mcp-ui-links.md)) |
| Windsurf | Agent-specific MCP config |
The unified server handles both feature management and AgentControl, so only one server entry is needed.
**Add the unified LaunchDarkly server for onboarding.** This single server handles feature flags and AgentControl.
### Step 4: Agent-Specific Authorization
@@ -82,44 +86,72 @@ After writing the config, some agents need extra steps. **Do not** send users th
- Click **Save** after adding the MCP configuration in repo settings. Use the [GitHub Copilot MCP doc](https://docs.github.com/en/copilot/customizing-copilot/extending-copilot-coding-agent-with-mcp) for the exact **Settings** path on github.com.
### Step 5: Enable and Verify
### Step 5: Verify MCP Tools (No Mandatory Restart)
After adding the config, the user needs to enable and authorize the server. MCP tools may become available immediately in some agents (Cursor, Claude Code) without a restart.
Restart is no longer required for Cursor or Claude Code after enabling an MCP server. Probe for tools immediately after the user confirms they've enabled and authorized the server.
1. **Tell the user to enable the server.** They need to toggle on the LaunchDarkly server and complete OAuth in their editor's MCP settings (e.g. in Cursor: toggle on the server and click **Connect**).
2. **Probe immediately.** After the user confirms they've enabled the server, call a lightweight MCP tool (e.g. `list-feature-flags` with the known project key). Do not ask the user whether MCP is working — just try it.
- **Success** (normal response, even an empty flag list): MCP is live. Note it in the onboarding log and continue.
- **Failure** (tool not found, auth error, timeout): **update the onboarding log first** (set Step 4 to "in progress - pending restart", Next step to "Step 4: Verify MCP after restart"), then suggest a restart with clear resume instructions:
> "MCP tools aren't available yet. Try restarting your editor. When you come back, just say **'continue LaunchDarkly onboarding'** — I'll pick up where we left off using the onboarding log."
3. **If restart doesn't help**, fall back to ldcli/API for Steps 5-6. Note the fallback in the onboarding log. Do **not** block the rest of onboarding.
4. If the failure looks like a config issue (wrong file path, missing OAuth, server not enabled), mention the likely cause so the user can fix it on their own time — but do not block progress.
1. **Tell the user to enable and authorize the server.** In Cursor: toggle on the LaunchDarkly server in MCP settings and click **Connect**. In Claude Code: the OAuth prompt appears on first tool call. Do **not** tell them to restart yet.
2. **Probe immediately.** After the user confirms the server is enabled, call a lightweight MCP tool (e.g. `list-feature-flags` with the user's project key). Do not ask the user whether MCP is working — just try it.
- **Success** (normal response, even an empty flag list): MCP is live. Continue.
- **Auth error** (401, 403, "unauthorized", "forbidden", or OAuth-related message): the server was found but authorization failed. Go to step 3a.
- **Tool not found or timeout** (tool not recognized, connection refused, no response): the editor hasn't picked up the server yet. Go to step 3b.
3a. **Auth failure path.** The MCP server is reachable but OAuth is incomplete or expired — restarting the editor won't help.
- Tell the user: "The MCP server responded but authorization failed. In Cursor: open MCP settings, find the LaunchDarkly server, and click **Connect** to re-authorize. In Claude Code: the next MCP tool call should re-trigger the OAuth prompt."
- Use [MCP UI links](references/mcp-ui-links.md) to give the user a direct shortcut to their agent's MCP settings.
- Re-probe after the user confirms they re-authorized.
- If the re-probe succeeds: continue with onboarding.
- If it fails again: offer the retry one-liner (see step 4 below). Do **not** suggest a restart for a persistent auth problem.
3b. **Server-not-found path.** The editor likely hasn't loaded the new MCP config yet.
- Tell the user: "The MCP tools aren't visible yet. Some editors need a restart to pick up new MCP servers. Restart your editor and say **'continue LaunchDarkly onboarding'** when you're back — I'll resume from here."
- Be specific about how to restart: "Restart Cursor" / "reload Claude Code" / "refresh the Copilot agent" depending on what you detected in Step 1.
4. **On resume after restart:** The parent onboarding skill detects live state, so no log file is needed. When the next turn starts:
- Re-probe for MCP tools silently.
- If tools are now available: "MCP is connected." Continue with onboarding.
- If tools still missing: do **not** block the rest of onboarding — remaining steps must still be completable without MCP. Offer a one-liner to retry later: "You can set up MCP anytime by clicking [quick install link] and restarting."
5. If the failure looks like a config issue (wrong file path, server not enabled), mention the likely cause so the user can fix it on their own time — but do not block progress.
## Edge Cases
- **User already has MCP configured:** Verify by checking for existing LD MCP entries in the config.
- `mcp/launchdarkly` → working, skip configuration
- `mcp/fm` or `mcp/aiconfigs` → deprecated, ask before migrating:
- **User already has MCP configured:** Verify by checking for existing LD MCP entries in the config. If the unified server (`mcp/launchdarkly`) is present and working, skip configuration. If the deprecated `mcp/fm` or `mcp/aiconfigs` is present, see below.
- **User has the deprecated `mcp/aiconfigs` or `mcp/fm` server:** These URLs are deprecated. Do **not** auto-migrate. Use a blocking question:
**D-MIGRATE -- BLOCKING:** Call your structured question tool now.
- question: "I see you have a deprecated MCP server configured (`mcp/fm` and/or `mcp/aiconfigs`). Those endpoints are deprecated — the unified server at `mcp/launchdarkly` now handles both feature management and AgentControl. Want me to update your config?"
- options:
- "Yes, update my config to use the unified server"
- "No, leave it as is for now"
- STOP. Do not modify the MCP config before the user selects an option.
```json
{
"questions": [
{
"id": "legacy_migration",
"prompt": "I found a deprecated LaunchDarkly MCP URL in your config (mcp/fm or mcp/aiconfigs). It should be replaced with the unified LaunchDarkly server (mcp/launchdarkly), which handles both feature flags and AgentControl. Do you want me to migrate?",
"options": [
{ "id": "yes", "label": "Yes, remove the old server and add the unified one" },
{ "id": "no", "label": "No, leave it as is for now" }
]
}
]
}
```
If they agree, remove the deprecated entries and ensure the unified `mcp/launchdarkly` config is present. See [MCP Config Templates](references/mcp-config-templates.md). If they decline, note the deprecation and continue.
- **User has the old npx-based local server:** Migrate them. Remove the old `npx @launchdarkly/mcp-server` entry and any `LD_ACCESS_TOKEN` env vars. Replace with the hosted server config. See [MCP Config Templates — Migration](references/mcp-config-templates.md#migrating-from-old-configurations).
- If **yes**: remove the deprecated entry, ensure the unified `mcp/launchdarkly` server is present (do not duplicate if it's already there), and continue.
- If **no**: leave the config untouched and continue with onboarding — the deprecated server may still work for now.
- **User has the old npx-based local server:** Migrate them to the hosted server. Remove the old `npx @launchdarkly/mcp-server` entry and any `LD_ACCESS_TOKEN` env vars. Replace with the hosted server config.
- **Agent not in known list:** Provide the generic pattern: the user needs to add an MCP server entry pointing to `https://mcp.launchdarkly.com/mcp/launchdarkly` using whatever format their agent expects.
- **User opts out of MCP during onboarding:** Document that choice and continue with the parent skill's ldcli/API fallbacks for environments and flags; do not block SDK work.
- **User opts out of MCP during onboarding:** Document that choice and continue; do not block SDK work.
## What NOT to Do
- Don't configure the old npx-based local server. Use the hosted server.
- Don't ask for or store API keys for the hosted server. The hosted server uses OAuth.
- Don't configure the old separate FM/AgentControl servers. Use the unified `mcp/launchdarkly` server.
- Don't configure a local npx-based server. Always use the hosted server.
- Don't ask for or store API keys. The hosted server uses OAuth.
- Don't auto-migrate from the deprecated `mcp/aiconfigs` — always ask via the blocking question.
- Don't suggest restart as the first step — probe for tools immediately after the user enables the server.
- Don't suggest restart for auth errors (401/403) — the server was found, so a restart won't help. Guide the user to re-authorize instead.
## References
- [MCP UI links](references/mcp-ui-links.md) — HTTPS + `command:` links to open MCP settings (Cursor, VS Code, Claude Code, Windsurf, GitHub)
- [MCP Config Templates](references/mcp-config-templates.md) — hosted OAuth JSON per agent; migration from old configurations
- [MCP Config Templates](references/mcp-config-templates.md) — hosted OAuth JSON per agent; migration from deprecated configs
- [Official MCP docs](https://launchdarkly.com/docs/home/getting-started/mcp-hosted) — full hosted setup guide
@@ -4,10 +4,26 @@ Per-agent JSON snippets for configuring the LaunchDarkly hosted MCP server. All
Source: https://launchdarkly.com/docs/home/getting-started/mcp-hosted
## Unified Server (Recommended)
Use the unified server URL for new configurations:
| Server | URL | Purpose |
|--------|-----|---------|
| LaunchDarkly (unified) | `https://mcp.launchdarkly.com/mcp/launchdarkly` | Feature flags and AgentControl |
**Legacy URLs (deprecated):**
- `mcp/fm`**deprecated**. Migrate to the unified server.
- `mcp/aiconfigs`**deprecated**. Migrate to the unified server.
See [mcp-configure Edge Cases](../SKILL.md#edge-cases) for migration guidance.
## Cursor
Config file: `.cursor/mcp.json` in the project root.
### Unified server (recommended)
```json
{
"mcpServers": {
@@ -19,12 +35,29 @@ Config file: `.cursor/mcp.json` in the project root.
}
```
### Legacy feature management only (deprecated)
> **Deprecated:** `mcp/fm` is deprecated. Use the unified server URL above.
```json
{
"mcpServers": {
"LaunchDarkly feature management": {
"url": "https://mcp.launchdarkly.com/mcp/fm",
"headers": {}
}
}
}
```
**After adding the config:** enable the server and complete OAuth in Cursor's MCP UI. Use [MCP UI links — Cursor](mcp-ui-links.md#clients) (HTTPS doc + optional `command:` links); do not rely only on nested Settings menu paths.
## Claude Code
Config file: `.mcp.json` in the project root, or `~/.claude.json` for global config.
### Unified server (recommended)
```json
{
"mcpServers": {
@@ -36,6 +69,21 @@ Config file: `.mcp.json` in the project root, or `~/.claude.json` for global con
}
```
### Legacy feature management only (deprecated)
> **Deprecated:** `mcp/fm` is deprecated. Use the unified server URL above.
```json
{
"mcpServers": {
"LaunchDarkly feature management": {
"type": "http",
"url": "https://mcp.launchdarkly.com/mcp/fm"
}
}
}
```
Authorization happens automatically via OAuth prompt on first MCP tool call.
## GitHub Copilot
@@ -75,11 +123,74 @@ Windsurf uses a similar MCP configuration format. Add to the agent's MCP config:
Consult Windsurf's documentation for the exact config file location.
## Migrating from Old Configurations
## Migrating from Deprecated mcp/fm
### From the old local npx-based server
The `mcp/fm` URL is deprecated. Replace it with the unified server.
If the user has the old npx-based server configured, replace it:
**Remove this:**
```json
{
"mcpServers": {
"LaunchDarkly feature management": {
"url": "https://mcp.launchdarkly.com/mcp/fm",
"headers": {}
}
}
}
```
**Replace with:**
```json
{
"mcpServers": {
"LaunchDarkly": {
"url": "https://mcp.launchdarkly.com/mcp/launchdarkly",
"headers": {}
}
}
}
```
## Migrating from Deprecated mcp/aiconfigs
The `mcp/aiconfigs` URL is deprecated. Replace it with the unified server.
**Remove this:**
```json
{
"mcpServers": {
"LaunchDarkly AI Configs": {
"url": "https://mcp.launchdarkly.com/mcp/aiconfigs",
"headers": {}
}
}
}
```
**Replace with:**
```json
{
"mcpServers": {
"LaunchDarkly": {
"url": "https://mcp.launchdarkly.com/mcp/launchdarkly",
"headers": {}
}
}
}
```
The unified server handles both feature flags and AgentControl. If you also had `mcp/fm`, migrate it to the unified server — `mcp/fm` is **deprecated** and will be removed.
**Note:** Do not auto-migrate. Always ask the user via a blocking question before making changes (see [mcp-configure Edge Cases](../SKILL.md#edge-cases)).
## Migrating from the Old Local Server
If the user has the old npx-based server configured with an inline API key, replace it with the hosted unified server:
**Remove this:**
@@ -98,29 +209,17 @@ If the user has the old npx-based server configured, replace it:
}
```
**Replace with the hosted config for the relevant agent** (see sections above).
Also remove any `LD_ACCESS_TOKEN` or `LAUNCHDARKLY_API_KEY` environment variables that were used for the local server. The hosted server handles authentication via OAuth.
### From deprecated split servers (`mcp/fm` and `mcp/aiconfigs`)
Both `mcp/fm` and `mcp/aiconfigs` are deprecated. All functionality is now in the unified server (`mcp/launchdarkly`).
If the user has either endpoint configured, **ask before removing** — see the edge case flow in [SKILL.md](../SKILL.md#edge-cases). The user should confirm the migration.
**Entries to remove (after user confirms):**
**Replace with the unified hosted server:**
```json
{
"mcpServers": {
"LaunchDarkly Feature Management": {
"url": "https://mcp.launchdarkly.com/mcp/fm"
},
"LaunchDarkly AgentControl": {
"url": "https://mcp.launchdarkly.com/mcp/aiconfigs"
"LaunchDarkly": {
"url": "https://mcp.launchdarkly.com/mcp/launchdarkly",
"headers": {}
}
}
}
```
**Replace with the single unified server** (see sections above).
Also remove any `LD_ACCESS_TOKEN` or `LAUNCHDARKLY_API_KEY` environment variables that were used for the local server. The hosted server handles authentication via OAuth.
-127
View File
@@ -1,127 +0,0 @@
---
title: Onboarding Summary
description: Generate a setup summary document the user can reference, with links to documentation and suggested next steps
---
# Onboarding Summary
After completing the onboarding flow, leave behind a summary document in the user's repository so they (and their team) have a reference for how LaunchDarkly was set up and what to do next.
## Step 1: Generate the Summary Document
Create a file called `LAUNCHDARKLY.md` (or `docs/LAUNCHDARKLY.md` if the project has a `docs/` directory) in the user's repository with the following sections. Fill in the details based on what was done during onboarding. Collect the LaunchDarkly **project key** and **environment key** (`{PROJECT_KEY}` / `{ENV_KEY}`) from the same place you used during onboarding (dashboard URLs, MCP tools, or **Project settings → Environments**) if they were not written into the plan explicitly.
### Template
The wrapper below uses `~~~markdown` so a nested ` ```json ` block inside the template does not break Markdown rendering. (The generated `LAUNCHDARKLY.md` file itself may use normal ` ``` ` fences.)
~~~markdown
# LaunchDarkly Setup
This project uses [LaunchDarkly](https://launchdarkly.com) for feature flag management.
## SDK Details
- **SDK**: {SDK_NAME} ({SDK_PACKAGE})
- **SDK Type**: {server-side | client-side | mobile | edge}
- **Key Type**: {SDK Key | Client-side ID | Mobile Key}
- **Installed via**: {INSTALL_COMMAND}
- **Initialization file**: {ENTRYPOINT_FILE}
## Configuration
The SDK key is configured via the `{ENV_VAR_NAME}` environment variable.
- **Do not hardcode** the SDK key in source code.
- Add the key to your `.env` file locally (already in `.gitignore`).
- For production, set it in your deployment environment (e.g., CI/CD secrets, container env vars, cloud config).
## Where to Find Things
| What | Where |
|------|-------|
| Feature flags dashboard | https://app.launchdarkly.com/projects/{PROJECT_KEY}/flags |
| Project settings | https://app.launchdarkly.com/settings/projects/{PROJECT_KEY} |
| Environments | https://app.launchdarkly.com/settings/projects/{PROJECT_KEY}/environments |
| API access tokens | https://app.launchdarkly.com/settings/authorization |
| SDK documentation | {SDK_DOCS_URL} |
| LaunchDarkly docs | https://launchdarkly.com/docs |
## How Feature Flags Work in This Project
1. Flags are evaluated using the LaunchDarkly SDK in `{ENTRYPOINT_FILE}`
2. Flag values are fetched from LaunchDarkly based on the evaluation context (user/device/org)
3. Changes to flags in the dashboard take effect immediately (server-side SDKs use streaming by default)
### Example: Evaluating a Flag
{INSERT_LANGUAGE_SPECIFIC_EXAMPLE}
## Next Steps
Here are some things you can do now that LaunchDarkly is set up:
### Feature Flag Best Practices
- **Use flags for every new feature**: Wrap new features in flags so you can release and roll back independently of deployments.
- **Clean up temporary flags**: Mark flags as temporary during creation and archive them when no longer needed.
- **Use descriptive flag keys**: e.g., `enable-checkout-v2` instead of `flag-1`.
### Advanced Capabilities
- **[Percentage Rollouts](https://launchdarkly.com/docs/home/targeting-flags/rollouts)** — Gradually roll out features to a percentage of users.
- **[Targeting Rules](https://launchdarkly.com/docs/home/targeting-flags/targeting-rules)** — Target specific users, segments, or contexts.
- **[Experimentation](https://launchdarkly.com/docs/home/about-experimentation)** — Run A/B tests and measure the impact of flag variations.
- **[configs](https://launchdarkly.com/docs/home/ai-configs)** — Manage AI model configurations and prompts with feature flags.
- **[Guarded Rollouts](https://launchdarkly.com/docs/home/guarded-rollouts)** — Automatically roll back flag changes based on metric guardrails.
- **[Observability](https://launchdarkly.com/docs/home/observability)** — Monitor flag evaluations and SDK performance with built-in telemetry.
### Agent Integration (MCP Server)
Install the [LaunchDarkly MCP server](https://github.com/launchdarkly/mcp-server) to let your agent manage feature flags directly from your editor. With it, your agent can:
- **Create and manage flags** — Ask your agent to create a new feature flag, and it will handle the API calls for you.
- **Toggle flags on/off** — Turn features on or off across environments without leaving your editor.
- **Set up targeting rules** — Configure percentage rollouts, user targeting, and segment-based rules through natural language.
- **Clean up stale flags** — Ask your agent to find temporary flags that are fully rolled out and ready to archive.
- **Run experiments** — Set up A/B tests and monitor results through your agent.
- **Manage configs** — Update model configurations and prompts managed by LaunchDarkly.
**Setup:** Use the [Hosted MCP](https://launchdarkly.com/docs/home/getting-started/mcp-hosted) server, which uses OAuth — no tokens stored in config files.
See the [MCP server docs](https://github.com/launchdarkly/mcp-server) for editor-specific setup instructions.
### Useful CLI Commands
If you have `ldcli` installed:
| Command | Description |
|---------|-------------|
| `ldcli flags list --project {PROJECT_KEY}` | List all feature flags |
| `ldcli flags toggle-on --project {PROJECT_KEY} --environment {ENV_KEY} --flag FLAG_KEY` | Turn a flag on |
| `ldcli flags create --project {PROJECT_KEY} --data '{"name": "My Flag", "key": "my-flag", "kind": "boolean"}'` | Create a new flag |
| `ldcli environments list --project {PROJECT_KEY}` | List environments and SDK keys |
~~~
## Step 2: Fill in the Template
Replace all `{PLACEHOLDER}` values with the actual values from the onboarding session (gather any you did not write down earlier from **Project settings → Environments** in LaunchDarkly or from MCP tools / `ldcli`):
- `{SDK_NAME}`: The human-readable SDK name (e.g., "Node.js Server SDK")
- `{SDK_PACKAGE}`: The package name (e.g., `@launchdarkly/node-server-sdk`)
- `{INSTALL_COMMAND}`: The install command used (e.g., `npm install @launchdarkly/node-server-sdk`)
- `{ENTRYPOINT_FILE}`: The file where initialization code was added
- `{ENV_VAR_NAME}`: The environment variable name used for the SDK key (or client-side ID env name)
- `{PROJECT_KEY}`: The LaunchDarkly **project** key (URL segment and `ldcli --project`)
- `{ENV_KEY}`: The LaunchDarkly **environment** key for the environment whose SDK key / client-side ID you used (e.g., `production`, `test`, `development`)—required for `ldcli` commands that take `--environment` and for the dashboard links that are scoped per environment where applicable
- `{SDK_DOCS_URL}`: Link to the specific SDK documentation
- `{INSERT_LANGUAGE_SPECIFIC_EXAMPLE}`: A short code snippet showing flag evaluation in the project's language
## Step 3: Commit the Summary
Add the file to version control so the whole team can reference it:
```bash
git add LAUNCHDARKLY.md
git commit -m "docs: add LaunchDarkly setup reference"
```
Ask the user for permission before committing. If they prefer not to commit it, that's fine — they still have the file locally.
@@ -1,172 +0,0 @@
---
title: Editor Rules and Skills
description: Leave behind editor-specific rules that point agents at LaunchDarkly agent skills (not doc URLs) for ongoing feature flag work
---
# Editor Rules and Skills
After onboarding, do two things:
1. **Ensure the user has the relevant LaunchDarkly agent skills installed** — The goal is not “a plugin” in the abstract: the user (or agent) should have the **same skills this repository ships**, so future sessions can load them by `name`. **Standard set (install all four):**
- `launchdarkly-flag-create`
- `launchdarkly-flag-discovery`
- `launchdarkly-flag-targeting`
- `launchdarkly-flag-cleanup`
**Optional:** `onboarding` (for more SDK onboarding later).
**Preferred:** Install the **LaunchDarkly** plugin **`launchdarkly@launchdarkly-ai-tooling`** from **[github.com/launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling)**, which packages these skills—then confirm in the UI or CLI that those skill names are available.
- **Claude Code:** After any required marketplace setup for that repo (see the repository README or `claude plugin` / Anthropic docs for your CLI version), install with:
```bash
claude plugin install launchdarkly@launchdarkly-ai-tooling
```
If the command name or flags differ in your build, use `claude plugin --help` and match the plugin coordinate above. **Verify** the four standard skills (above) show up as usable after install.
- **Cursor:** Install or enable skills from the same distribution per current Cursor docs (marketplace / plugin UI pointing at this repo or copied folders). **Verify** the four `launchdarkly-flag-*` skills are listed or discoverable.
- **Fallback (no plugin):** Copy skill directories from [github.com/launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling) (or this monorepos published plugin) into the users skills location so each folder contains a `SKILL.md` for `launchdarkly-flag-create`, `launchdarkly-flag-discovery`, `launchdarkly-flag-targeting`, and `launchdarkly-flag-cleanup`. Optional: include **`onboarding`** from this repos onboarding path. List any copied paths in the rules file so agents know where to read `SKILL.md`.
2. **Write an editor rule** — The rule must tell *future* agents to **read and follow** those skills for flag work. Do **not** bake in SDK documentation URLs; procedures and compatibility live in each skills `SKILL.md`. Deep links to SDK docs belong in `LAUNCHDARKLY.md` if you already created that summary.
## Default skill set (feature flags)
Unless the user opts out, treat **all** of these as the standard kit and mention each one in the generated rule. They are published in [github.com/launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling) (same four skills as in step 1 above):
| Skill `name` (frontmatter) | Purpose |
|----------------------------|---------|
| `launchdarkly-flag-create` | Create and configure flags and wire evaluation to match the codebase (MCP when required). |
| `launchdarkly-flag-discovery` | Audit flag inventory, stale or launched flags, and removal readiness. |
| `launchdarkly-flag-targeting` | Toggle flags, percentage rollouts, targeting rules, and promote config between environments. |
| `launchdarkly-flag-cleanup` | Safely remove flags from code, readiness checks, and MCP-driven cleanup workflows. |
Adjust the table only if the user explicitly wants a smaller set.
## Step 1: Detect the Editor
Check for editor configuration files in the project root:
| File/Directory | Editor |
|----------------|--------|
| `.cursor/` or `.cursorrules` | Cursor |
| `.claude/` | Claude Code (Anthropic) |
| `.github/copilot-instructions.md` | GitHub Copilot |
| `.vscode/` (without Cursor indicators) | VS Code |
| `.idea/` | JetBrains IDE — use [For JetBrains](#for-jetbrains-intellij-webstorm-rider-etc) (no dedicated template file path here) |
If you can't detect the editor, default to Claude Code and create `.claude/rules/launchdarkly.md`.
## Step 2: Create the Rules File
Base the file on the templates below. **Substitute project facts** (`{SDK_NAME}`, `{ENTRYPOINT_FILE}`, `{ENV_VAR_NAME}`) from onboarding. **Do not** add `{SDK_DOCS_URL}` or a documentation links section to this file—that is intentionally omitted so agents rely on skills + MCP.
The rule text must explicitly say: *when doing flag create, targeting, discovery, cleanup, or code removal, load the matching LaunchDarkly skill and execute its workflow* (not just “see links”).
### Shared body (use in Cursor and Claude Code)
Use this markdown block inside each template (Cursor: below the YAML frontmatter; Claude Code: from the first `#` heading).
```markdown
# LaunchDarkly Feature Flags
This project uses LaunchDarkly for feature flag management.
## SDK context (this repo)
- SDK: {SDK_NAME}
- Initialization: {ENTRYPOINT_FILE}
- Key env var: {ENV_VAR_NAME} (never hardcode secrets in source)
## Agent: use LaunchDarkly skills (required)
Ensure the **LaunchDarkly** agent skills below are installed (`launchdarkly@launchdarkly-ai-tooling` plugin from [github.com/launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling), or equivalent copies on disk). For any substantive flag work, **open that skills `SKILL.md` and follow it**—do not improvise from generic flag advice alone.
| Skill | When to use it |
|-------|------------------|
| `launchdarkly-flag-create` | User wants a new flag, code wiring, feature toggle, or experiment setup. |
| `launchdarkly-flag-discovery` | User wants flag inventory, debt/stale-flag audit, health, or removal readiness. |
| `launchdarkly-flag-targeting` | User wants who sees a flag, rollouts, targeting rules, or environment promotion. |
| `launchdarkly-flag-cleanup` | User wants a flag removed from code safely, archive/cleanup workflows, or MCP-driven removal. |
**Invocation:** Match the users request to the skill `description` in each skills frontmatter, or use the editors slash / plugin command for that skill if configured.
**Tools:** When a skill lists LaunchDarkly MCP tools as required, use MCP; do not skip validation steps.
## Conventions (summary)
- Prefer boolean flags unless multivariate is required; use descriptive kebab-case keys (e.g. `enable-checkout-v2`).
- Always pass a fallback when evaluating flags; use a meaningful evaluation context (user key, org, etc.).
- Server-side SDK keys stay secret; client-side IDs may appear in browser code.
- Do not evaluate flags in tight loops without caching.
- Archive or remove flag code when a flag is fully rolled out and the team agrees—use `launchdarkly-flag-cleanup` (and `launchdarkly-flag-discovery` first if assessing candidates).
```
### For Cursor (`.cursor/rules/launchdarkly.mdc`)
Create `.cursor/rules/launchdarkly.mdc`:
```markdown
---
description: LaunchDarkly feature flags — require LaunchDarkly agent skills for flag workflows
globs: []
alwaysApply: false
---
{PASTE_SHARED_BODY_HERE}
```
Replace `{PASTE_SHARED_BODY_HERE}` with the [shared body](#shared-body-use-in-cursor-and-claude-code) (no literal placeholder left in the file).
### For Claude Code (`.claude/rules/launchdarkly.md`)
Create `.claude/rules/launchdarkly.md` containing **only** the [shared body](#shared-body-use-in-cursor-and-claude-code) (no YAML frontmatter).
### For GitHub Copilot (`.github/copilot-instructions.md`)
Append to `.github/copilot-instructions.md` (create if it doesn't exist). Copilot may not load external skills the same way; still steer toward the same workflows and name the skills:
```markdown
## LaunchDarkly Feature Flags
This project uses LaunchDarkly ({SDK_NAME}) for feature flag management.
Initialization: {ENTRYPOINT_FILE}. SDK key / client ID: environment variable `{ENV_VAR_NAME}` only—never commit secrets.
For flag work, follow the LaunchDarkly agent skills when available: **`launchdarkly-flag-create`** (create + code), **`launchdarkly-flag-discovery`** (audit / inventory), **`launchdarkly-flag-targeting`** (rollouts / targeting / env promotion), **`launchdarkly-flag-cleanup`** (code removal / cleanup / MCP workflows). Read each skills instructions instead of guessing flag lifecycle steps.
```
### For JetBrains (IntelliJ, WebStorm, Rider, etc.)
There is **no** JetBrains-specific rules template in this reference (`.idea/` only indicates the IDE family). **Do not silently skip:** tell the user you detected JetBrains and that they should rely on **`LAUNCHDARKLY.md`** ([Onboarding Summary](1.8-summary.md)) plus installing **`launchdarkly@launchdarkly-ai-tooling`** (same as Claude Code) or copying skill folders manually.
Reasonable options to suggest:
- Keep flag workflow guidance in **`LAUNCHDARKLY.md`** and team docs the IDE already opens.
- If the team also uses GitHub Copilot in the same repo, reuse the **For GitHub Copilot** template above in `.github/copilot-instructions.md` so any tool that reads that file picks up the same guidance.
- If their JetBrains AI plugin supports a **project-level instruction file**, paste the [shared body](#shared-body-use-in-cursor-and-claude-code) there (adapt paths to your products docs)—this doc does not name a single standard path for all JetBrains products.
### For other editors (not Cursor, Claude Code, Copilot, VS Code, or JetBrains)
If the editor doesn't have a rules system, skip creating a rules file. Rely on `LAUNCHDARKLY.md` and tell the user which LaunchDarkly skills to install (`launchdarkly@launchdarkly-ai-tooling` or copied skill folders from [github.com/launchdarkly/ai-tooling](https://github.com/launchdarkly/ai-tooling)).
## Step 3: Fill in the Placeholders
From the onboarding session, set:
- `{SDK_NAME}` — e.g. `Node.js Server SDK`
- `{ENTRYPOINT_FILE}` — e.g. `src/index.ts`
- `{ENV_VAR_NAME}` — e.g. `LAUNCHDARKLY_SDK_KEY`
**Do not** add documentation URLs to this rules file. If you need SDK doc links for humans, put them only in `LAUNCHDARKLY.md` ([Onboarding Summary](1.8-summary.md)).
If you added or removed skills from the default table (user request), update the skill table in the shared body to match.
## Step 4: Commit the Rules
```bash
# Claude Code (most common for this skill)
git add .claude/rules/launchdarkly.md
# Cursor
git add .cursor/rules/launchdarkly.mdc
# GitHub Copilot / shared instructions
git add .github/copilot-instructions.md
# Add only the file(s) you created or changed, then:
git commit -m "chore: add LaunchDarkly feature flag management rules"
```
Ask the user for permission before committing.
@@ -9,7 +9,7 @@ description: Onboarding sample and links for the LaunchDarkly Node.js server-sid
- API reference: [SDK API docs](https://launchdarkly.github.io/js-core/packages/sdk/server-node/docs/)
- Recipe (detect / install): [SDK Recipes](../recipes.md) (Node.js Server)
**Includes:** Patterns below follow the **Get started**, **Initialize the client**, **Evaluate a context**, and **Promises and async** sections of the [Node.js server-side SDK reference](https://launchdarkly.com/docs/sdk/server-side/node-js). Use **one** initialization strategy (`waitForInitialization` **or** the `ready` event—not both at once unless you know why). Aligns with [Create First Feature Flag](../../../first-flag/SKILL.md) (evaluation uses **context** + default).
**Includes:** Patterns below follow the **Get started**, **Initialize the client**, **Evaluate a context**, and **Promises and async** sections of the [Node.js server-side SDK reference](https://launchdarkly.com/docs/sdk/server-side/node-js). Use **one** initialization strategy (`waitForInitialization` **or** the `ready` event—not both at once unless you know why).
### Singleton client
+4 -4
View File
@@ -1,6 +1,6 @@
---
name: sdk-install
description: "Install and initialize the correct LaunchDarkly SDK during onboarding by running nested skills in order: detect, plan, apply. Parent onboarding Step 6 is first flag."
description: "Install and initialize the correct LaunchDarkly SDK during onboarding by running nested skills in order: detect, plan, apply. Parent onboarding Step 4 is first flag."
license: Apache-2.0
compatibility: Requires a supported language/framework in the project. SDK credentials are required by [Apply](apply/SKILL.md), not for [Detect](detect/SKILL.md) / [Plan](plan/SKILL.md) alone (see parent onboarding **Prerequisites**).
metadata:
@@ -10,12 +10,12 @@ metadata:
# LaunchDarkly SDK Install (onboarding)
Installs and initializes the right LaunchDarkly SDK for the users project by following **three nested skills in order**. **Do not** skip ahead to feature flags here—the parent [LaunchDarkly onboarding](../SKILL.md) continues with **Step 6: First feature flag** using [Create first feature flag](../first-flag/SKILL.md).
Installs and initializes the right LaunchDarkly SDK for the users project by following **three nested skills in order**. **Do not** skip ahead to feature flags here—the parent [LaunchDarkly onboarding](../SKILL.md) continues with **Step 4: First flag**.
## Prerequisites
- Project context from parent **Step 1: Explore the Project** (reuse it; only re-run deep detection if something is unclear)
- **SDK key / client-side ID / mobile key:** Needed when you reach [Apply code changes](apply/SKILL.md) (env wiring). **Do not** ask the user for these during detect or plan solely because you opened this skill—follow parent onboarding: account status is inferred via MCP OAuth (Step 4) or surfaced at D7 in apply; key material is collected at apply (see parent [Prerequisites](../SKILL.md#prerequisites)).
- **SDK key / client-side ID / mobile key:** Needed when you reach [Apply code changes](apply/SKILL.md) (env wiring). **Do not** ask the user for these during detect or plan solely because you opened this skill—follow parent onboarding: account status is inferred via MCP OAuth when MCP is already configured, or surfaced at D7 in apply; key material is collected at apply (see parent [Prerequisites](../SKILL.md#prerequisites)).
## Key types (summary)
@@ -43,7 +43,7 @@ Shared references for all steps: [SDK recipes](../references/sdk/recipes.md), [S
Continue with the parent skill:
- **Step 6:** [Create first feature flag](../first-flag/SKILL.md)
- **Step 4:** [First flag](../SKILL.md#step-4-first-flag)
Do not add standalone “sample flag” evaluation in this skill unless the user explicitly needs a throwaway check; the parent flow creates the first flag in order.
+4 -4
View File
@@ -16,7 +16,7 @@ This skill is nested under [LaunchDarkly SDK Install (onboarding)](../SKILL.md);
**Dual SDK:** If the approved plan is **dual SDK** ([plan: Dual SDK integrations](../plan/SKILL.md#dual-sdk-integrations)), you must complete Steps 1-3 **for both tracks** -- **two** packages in the manifest, **two** install commands run (or equivalent), **two** credential lines where needed, **two** inits in **different** entrypoints per recipe. **Do not** claim the second SDK is set up without performing its real install and init. If the plan only listed one track but the user asked for both, **stop** and return to [plan](../plan/SKILL.md) -- do not invent the second half from memory.
**Credential timing:** This is the first nested step where you ask the user for **SDK key / client-side ID / mobile key** (or consent to fetch/write them). Account status is not asked upfront -- it is inferred earlier via MCP OAuth (parent Step 4) or surfaced here at D7 (option 4) if the user has no account yet ([parent Prerequisites](../../SKILL.md#prerequisites)).
**Credential timing:** This is the first nested step where you ask the user for **SDK key / client-side ID / mobile key** (or consent to fetch/write them). Account status is not asked upfront -- it is inferred earlier via MCP OAuth when MCP is already configured or surfaced here at D7 (option 4) if the user has no account yet ([parent Prerequisites](../../SKILL.md#prerequisites)).
## Step 1: Install the SDK dependency
@@ -60,7 +60,7 @@ If the user **declines** broader changes: keep only the LD package addition if p
- "I'll tell you where to put it"
- "I'll set up the secret myself -- just tell me what variable name to use"
- "Write it to a `.env` file for me"
- "I don't have an account yet -- help me sign up" -> point to the resolved signup URL (see [Source Attribution](../../SKILL.md#source-attribution); default `https://app.launchdarkly.com/signup?source=agent`), write placeholders and continue (real keys deferred until account is ready)
- "I don't have an account yet -- help me sign up" -> point to the resolved signup URL (see [Source Attribution](../../SKILL.md#source-attribution)), write placeholders and continue (real keys deferred until account is ready)
- STOP. Do not write the question as text. Do not fetch keys from LaunchDarkly or write real values into the repo without the user selecting an option first. Do not infer the answer from context or prior conversation -- always present this choice.
**If the user chooses option 1 ("Tell me where to put it"):**
@@ -81,7 +81,7 @@ If the user **declines** broader changes: keep only the LD package addition if p
3. Ensure `.env` is in `.gitignore` before writing any real values
**If the user chooses option 4 ("I don't have an account yet"):**
1. Point them to the resolved signup URL (see [Source Attribution](../../SKILL.md#source-attribution); default `https://app.launchdarkly.com/signup?source=agent`)
1. Point them to the resolved signup URL (see [Source Attribution](../../SKILL.md#source-attribution))
2. Explain that SDK key setup requires an account -- they can complete setup after signing up
3. Ensure `.env` is in `.gitignore` before writing (same check as option 3 / [Write to `.env`](#write-to-env-when-the-user-consents))
4. Write placeholder variable names to `.env` (no real values) so the code compiles
@@ -98,7 +98,7 @@ get-environments({ request: { projectKey: "PROJECT_KEY" } })
**Security: Treat MCP responses containing keys as sensitive.** Write keys only to the location the user chose without echoing full key values in chat responses. Keys in agent conversation history or logs may persist beyond the session.
Pick the correct key type from the matching environment in the response (see table below). If MCP is not configured, fall back to `ldcli` or the REST API (`GET /api/v2/projects/{PROJECT_KEY}/environments`).
Pick the correct key type from the matching environment in the response (see table below). If MCP is not configured, direct the user to the LaunchDarkly dashboard to retrieve their keys: `https://app.launchdarkly.com/projects/{PROJECT_KEY}/settings/environments/{ENV_KEY}/keys`.
### Variable names and where values come from
@@ -206,7 +206,7 @@ If they choose **both**, the plan must include **two** concrete integrations (se
After detection and confirmation:
- **No app found or unclear** --> Already handled by D5-NOAPP / D5-UNCLEAR in [section 5a](#5a-classify-workspace-confidence). Do not proceed to plan until the user confirms a real app target.
- **SDK already installed and initialized** --> Skip to [Create first feature flag](../../first-flag/SKILL.md) (parent Step 6)
- **SDK already installed and initialized** --> Skip to the parent skill's [Step 4: First Flag](../../SKILL.md#step-4-first-flag)
- **SDK installed but not initialized** --> Skip to [Apply code changes](../apply/SKILL.md) (just add init code)
- **SDK not present** --> Continue to [Generate integration plan](../plan/SKILL.md)
- **Multiple targets detected (e.g., frontend + backend)** --> If the user wants **both** SDKs (confirmed via D5 above), continue to [Generate integration plan](../plan/SKILL.md) with **dual-SDK** scope (two packages, two entrypoints). If they want **one** surface only, plan for that single SDK.