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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions .agents/skills/featureflags/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
name: featureflags
description: Provides step-by-step guidance for integrating feature flags into FeatBit MCP Server tools and code paths. Use when user asks about "add feature flag", "gate a tool", "control tool release", "McpToolFlagGate", "IFeatureFlagEvaluator", "ReleaseEnabled", "hide tool", "disable tool", or wants to control whether a feature or MCP tool is available at runtime. Do not use for general FeatBit REST API questions or questions about managing flags in the FeatBit dashboard.
license: MIT
metadata:
author: FeatBit
version: 1.0.0
category: sdk-integration
---

# Feature Flags Integration

Feature flags are a cross-cutting concern in this project (like logging). The entire feature flag system lives in `FeatBit.FeatureFlags/`. All infrastructure — SDK initialization, `IFeatureFlagEvaluator` registration, and the `tools/list` filter — is already wired in `Program.cs`. No extra setup is needed beyond Steps 1–2 of each approach below.

## When to Use

- **Gate an entire MCP tool** (hide it from `tools/list` when the flag is off) → Approach 1: `[McpToolFlagGate]`
- **Branch logic inside a tool or service** (switch endpoint, enable extra step, skip an action) → Approach 2: `IFeatureFlagEvaluator`

## Why Use Feature Flags

- Safe, gradual rollout of new tools and behaviour without redeployment.
- Per-session user targeting via `ISessionContext.SessionId`.
- Every evaluation is automatically traced in OpenTelemetry — no extra instrumentation needed.
- `DefaultValue: false` keeps new code dark until explicitly enabled in the FeatBit dashboard.

---

## Approach 1: Gate an MCP Tool with `[McpToolFlagGate]`

Use this to make a tool method invisible to MCP clients — as if the tool does not exist — when the flag is disabled.

### Step 1 — Declare the flag in `FeatureFlag.cs`

Add a `static readonly` field to `FeatBit.FeatureFlags/FeatureFlag.cs`:

```csharp
public static readonly FeatureFlag MyNewTool = new(
Key: "my-new-tool", // must match the key in the FeatBit dashboard exactly
DefaultValue: false, // tool is hidden when SDK is unavailable
Description: "Controls whether MyNewTool is exposed to MCP clients"
);
```

### Step 2 — Decorate the tool method

In your tool class inside `FeatBit.McpServer/Tools/`, add the attribute:

```csharp
[McpServerTool]
[Description("Does something new.")]
[McpToolFlagGate(nameof(FeatureFlag.MyNewTool))] // nameof keeps it refactoring-safe
public Task<string> MyNewTool() => apiClient.GetAsync("/api/v1/new-endpoint");
```

No further changes are needed. `AddMcpToolFlagGateFilter` is already registered in `Program.cs` and scans the assembly at startup.

---

## Approach 2: Control a Code Branch with `IFeatureFlagEvaluator`

Use this for conditional logic inside a method — e.g., choosing between two API endpoints, enabling an extra processing step, or conditionally triggering a side-effect.

### Step 1 — Declare the flag in `FeatureFlag.cs`

Same as Approach 1, Step 1.

### Step 2 — Inject `IFeatureFlagEvaluator`

Use primary constructor syntax in your tool class:

```csharp
[McpServerToolType]
public class MyTools(FeatBitApiClient apiClient, IFeatureFlagEvaluator flagEvaluator)
```

> `IFeatureFlagEvaluator` is registered as **scoped**. Never inject it into a singleton.

### Step 3 — Evaluate the flag

Pick the pattern that fits the situation:

**Async guard with fallback** — preferred for tool methods (`Task<string>` return):

```csharp
return await flagEvaluator.ReleaseEnabledThenAsync(
FeatureFlag.MyNewFeature,
async () => await apiClient.GetAsync("/api/v1/new-endpoint"),
await apiClient.GetAsync("/api/v1/legacy-endpoint") // returned when flag is off
);
```

**Simple boolean check** — use when the two branches differ significantly:

```csharp
if (flagEvaluator.ReleaseEnabled(FeatureFlag.MyNewFeature))
return await apiClient.GetAsync("/api/v1/new-endpoint");

return await apiClient.GetAsync("/api/v1/legacy-endpoint");
```

**Void side-effect guard** — run an action only when the flag is on:

```csharp
flagEvaluator.ReleaseEnabledThen(FeatureFlag.MyNewFeature, () => DoSomething());
```

---

## Choosing the Right Approach

| Goal | Approach |
|------|----------|
| Hide a tool completely from `tools/list` | `[McpToolFlagGate]` (Approach 1) |
| Switch between two async implementations | `ReleaseEnabledThenAsync` (Approach 2) |
| Simple if/else conditional logic | `ReleaseEnabled` (Approach 2) |
| Conditionally execute a void side-effect | `ReleaseEnabledThen` (Approach 2) |

---

## Rules

- Always use `nameof(FeatureFlag.SomeField)` in `[McpToolFlagGate]`— never a hardcoded string.
- Never call `IFeatureFlagEvaluator` from a singleton; it is scoped per request.
- Never add `try/catch` inside tool methods around flag evaluation — errors are handled by middleware.
- The flag `Key` must exactly match what is configured in the FeatBit dashboard.
- `DefaultValue: false` is the standard — features stay dark when the SDK is unavailable.

1 change: 0 additions & 1 deletion .copilot-commit-message-instructions.md

This file was deleted.

242 changes: 242 additions & 0 deletions .github/prompts/skill-builder-best-practice.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
---
name: skill-builder-best-practice
description: Describe when to use this prompt
---

<!-- Tip: Use /create-prompt in chat to generate content with agent assistance -->

# Skills Building Instruction

This repo contains agent skills for FeatBit. The primary task is authoring and maintaining `SKILL.md` files under `skills/`. All other work (releases, versioning) supports that purpose.

## Project Structure

```
featbit-skills/
├── .claude-plugin/
│ ├── plugin.json # version must stay in sync
│ └── marketplace.json # version must stay in sync
├── skills/
│ └── featbit-{topic}/
│ ├── SKILL.md # required, <500 lines
│ ├── references/ # one level deep only
│ ├── scripts/ # deterministic helper CLIs
│ └── assets/ # output templates, schemas
├── package.json # version must stay in sync
└── README.md # version badge must stay in sync
```

## Skill Authoring Rules

### Core Principle: Concise is Key

The context window is a shared resource. Only add context the agent doesn't already have. Challenge each piece of information:

- "Does the agent really need this explanation?"
- "Can I assume the agent knows this?"
- "Does this paragraph justify its token cost?"

At startup, only `name` and `description` are pre-loaded. `SKILL.md` is read only when the skill becomes relevant, and reference files are read only as needed.

### Directory and Naming

- Every skill directory must be named `featbit-{topic}`.
- The `name` field in `SKILL.md` frontmatter must exactly match the directory name (e.g., `name: featbit-dotnet-sdk` lives in `skills/featbit-dotnet-sdk/SKILL.md`).
- `references/` is one level deep only. Never create `references/db/v1/schema.md`; use `references/schema.md`.
- Never create `README.md`, `CHANGELOG.md`, or `INSTALLATION.md` inside a skill directory.

### Frontmatter

```yaml
---
name: featbit-{topic}
description: <trigger-optimized, max 1024 chars>
license: MIT
metadata:
author: FeatBit
version: 1.0.0
category: sdk-integration | deployment | api-reference | platform
---
```

`name` rules: 1–64 characters, lowercase letters, numbers, and hyphens, no consecutive hyphens. No reserved words: "anthropic", "claude".

`description` rules:
- Third person, describes capability and when to use it.
- Include explicit positive triggers (user phrases, file extensions, keywords).
- Include at least one negative trigger ("Do not use for...") to prevent false activation.
- No XML tags in the value.

Bad: `FeatBit SDK help.`

Good: `Expert guidance for integrating FeatBit .NET Server SDK. Use when user asks about ".NET SDK", "C# feature flags", "ASP.NET Core FeatBit", or mentions .cs, .csproj files. Do not use for Node.js, Python, or client-side JavaScript questions.`

### SKILL.md Content

- Hard limit: 500 lines. Move excess content to `references/`.
- `SKILL.md` is the navigation brain — quick start + decision routing only.
- Do not inline large code blocks, full config files, or exhaustive API schemas. Put them in `references/` or `assets/`.
- Load reference files just-in-time: instruct explicitly when to read them.

Just-in-time loading pattern:
```markdown
Read `references/environment-variables.md` only when the user asks about a specific variable or config option.
```

### Degrees of Freedom

Match instruction specificity to the task's fragility:

- **High freedom** (prose guidance): Use when multiple valid approaches exist or context determines the best path. Example: "Review the flag evaluation logic and suggest improvements."
- **Medium freedom** (pseudocode/templates with parameters): Use when a preferred pattern exists but some variation is acceptable. Example: provide a code template with placeholder comments.
- **Low freedom** (exact scripts, no variation): Use when operations are fragile or a specific sequence must be followed. Example: "Run exactly: `python scripts/migrate.py --verify --backup`. Do not modify the command."

### Writing Style

- Use numbered steps for every workflow. Define a strict chronological sequence.
- Write in imperative for workflow steps: "Extract the flag key from..." not "You should extract..."
- Description field must be third person: "Provides guidance for..." not "I can help you..."
- Map decision branches explicitly: "Step 3: If the user is on ASP.NET Core, proceed to Step 4. Otherwise skip to Step 5."
- Use one term per concept throughout the file. Never alternate between "feature flag", "flag", and "toggle".
- Use domain-native terminology (e.g., "dependency injection container" for .NET, not just "setup").
- Do not include time-sensitive information. Use `<details>` blocks for legacy/deprecated patterns.

### Reference Files

- Each reference file addresses one topic completely and is self-contained.
- Name descriptively: `references/openfeature-integration.md`, not `references/extra.md`.
- `SKILL.md` must state exactly when and why to open each reference file.
- References longer than 100 lines should include a table of contents at the top.
- Never nest references: all reference files link directly from `SKILL.md`. Never create `advanced.md` that points to `details.md`.

### Workflows

For multi-step tasks, provide a copyable checklist followed by per-step detail:

```markdown
## Setup workflow

Copy and track progress:
- [ ] Step 1: Install SDK
- [ ] Step 2: Configure environment
- [ ] Step 3: Initialize client
- [ ] Step 4: Evaluate first flag

**Step 1: Install SDK**
Run: `pip install fb-python-sdk`

**Step 2: Configure environment**
...
```

For error-prone operations, include a validation feedback loop: run validator → fix errors → repeat.

### Scripts

- Scripts in `scripts/` must be tiny single-purpose CLIs (Python, Bash, or Node).
- Scripts emit descriptive stdout/stderr so the agent can self-correct without user intervention.
- Do not bundle library or application code in scripts.
- Provide utility scripts for deterministic, repeatable operations — pre-made scripts are more reliable than generated code.
- Make execution intent explicit: "Run `validate.py`" (execute) vs "See `validate.py` for the algorithm" (read).

### MCP Tool References

When referencing MCP tools in a skill, always use the fully qualified format to avoid "tool not found" errors:

```
Use the FeatBit:get_feature_flags tool to retrieve flags.
```

Format: `ServerName:tool_name`

### What to Remove

- Instructions for tasks the agent can handle reliably without help.
- Prose explanations — convert to numbered steps or move to `references/`.
- Marketing text ("FeatBit is a powerful platform that...").
- Information already covered by another skill (e.g., auth basics from `featbit-rest-api` need not repeat in SDK skills).
- Time-sensitive content ("before August 2025, use X") — use deprecated sections instead.

### Anti-Patterns

- **Too many options**: Don't list 5 libraries. Pick one default and mention the alternative only when the default won't work.
- **Deeply nested references**: Never chain `SKILL.md → advanced.md → details.md`. Keep all references one level deep from `SKILL.md`.
- **Windows-style paths**: Always use forward slashes (`references/guide.md`), never backslashes.
- **Vague descriptions**: Avoid `description: Helps with FeatBit`. Be specific about what and when.

### New Skill Checklist

When adding a new skill, complete all steps in order:

1. Create `skills/featbit-{topic}/SKILL.md` with valid frontmatter.
2. Add entry to `package.json` → `skills` array.
3. Update skill count in `README.md` ("What is This?" section).
4. Add skill entry to `README.md` ("Available Skills" section).
5. Update `CHANGELOG.md`.
6. Bump MINOR version in `package.json`, `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, and `README.md` version badge.

### Pre-commit Validation

Verify before every skill commit:

- [ ] `name` in frontmatter exactly matches the directory name
- [ ] `name` contains no reserved words ("anthropic", "claude")
- [ ] Description has at least one negative trigger
- [ ] Description is written in third person
- [ ] `SKILL.md` is under 500 lines
- [ ] All files referenced in `SKILL.md` exist at the specified paths
- [ ] All paths use forward slashes and are relative
- [ ] No `README.md` or `CHANGELOG.md` inside the skill directory
- [ ] All workflow steps use imperative voice
- [ ] Reference files are one level deep from `SKILL.md` (no chained references)
- [ ] Reference files longer than 100 lines have a table of contents
- [ ] No time-sensitive date conditions in the content
- [ ] Skill is listed in `package.json` `skills` array

## Versioning

Follow Semantic Versioning (`MAJOR.MINOR.PATCH`).

| Change | Bump |
|--------|------|
| Breaking changes, removed skills, incompatible manifest changes | MAJOR |
| New skill, new feature, non-breaking enhancement | MINOR |
| Bug fix, typo, doc update, minor improvement | PATCH |

Keep version in sync across all four files simultaneously:
- `package.json`
- `.claude-plugin/plugin.json`
- `.claude-plugin/marketplace.json`
- `README.md` version badge

## Release

Release title format: `v{MAJOR}.{MINOR}.{PATCH} - {3–6 word description in Title Case}`

Examples: `v1.5.0 - AWS Deployment Support`, `v2.0.0 - Breaking Architecture Changes`

Release checklist:
- [ ] Version bumped in all four files
- [ ] `CHANGELOG.md` updated
- [ ] `git tag -a v{VERSION} -m "{title}"`
- [ ] `git push origin main && git push origin v{VERSION}`
- [ ] GitHub Release created with notes

## Commit Messages

Format: `<type>(<scope>): <subject>`

Types: `feat` (MINOR), `fix` (PATCH), `docs` (PATCH), `refactor` (PATCH), `chore` (no bump).

Scope is the skill name (e.g., `dotnet-sdk`, `deployment-docker`) or `release` for version bumps.

Examples:
```
feat(deployment-aws): add AWS ECS Fargate deployment skill
fix(dotnet-sdk): correct namespace in ASP.NET Core examples
chore(release): bump version to 1.5.0
```

## Language

Use English (US) for all skill content, commit messages, and documentation.
Loading
Loading