Skip to content

Commit d64e314

Browse files
docs: add EnableCondition migration guide for remote server
1 parent 3a52f7b commit d64e314

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed

docs/enable-condition-migration.md

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# EnableCondition Migration Guide for Remote Server
2+
3+
This guide explains how to adopt the composable `EnableCondition` system in `github/github-mcp-server-remote`.
4+
5+
## Go Module Update
6+
7+
To use this feature, update your `go.mod`:
8+
9+
```bash
10+
go get github.com/github/github-mcp-server@3a52f7bd4ba45a6799eb9209db09080428bf0878
11+
```
12+
13+
Or add directly to `go.mod`:
14+
```
15+
require github.com/github/github-mcp-server v0.0.0-20251218XXXXXX-3a52f7bd4ba4
16+
```
17+
18+
Then run `go mod tidy`.
19+
20+
> **Note**: Once PR #1641 is merged, you can use `@main` or wait for a tagged release.
21+
22+
## Quick Start
23+
24+
### Before (old approach with Enabled func)
25+
26+
```go
27+
tool := inventory.ServerTool{
28+
Tool: mcp.Tool{Name: "my_tool"},
29+
Enabled: func(ctx context.Context) (bool, error) {
30+
// CCA bypass pattern
31+
if isCCA(ctx) {
32+
return true, nil
33+
}
34+
// Otherwise check feature flag
35+
return checkFeatureFlag(ctx, "my_feature")
36+
},
37+
}
38+
```
39+
40+
### After (new approach with EnableCondition)
41+
42+
```go
43+
tool := inventory.ServerTool{
44+
Tool: mcp.Tool{Name: "my_tool"},
45+
EnableCondition: inventory.Or(
46+
inventory.ContextBool("is_cca"),
47+
inventory.FeatureFlag("my_feature"),
48+
),
49+
}
50+
```
51+
52+
## Available Primitives
53+
54+
```go
55+
import "github.com/github/github-mcp-server/pkg/inventory"
56+
57+
// Feature flag - checked via FeatureCheckerFromContext
58+
inventory.FeatureFlag("flag_name")
59+
60+
// Context bool - checked via ContextBoolFromContext
61+
inventory.ContextBool("key_name")
62+
63+
// Static values
64+
inventory.Always() // always enabled
65+
inventory.Never() // always disabled
66+
inventory.Static(true) // explicit bool
67+
```
68+
69+
## Combinators
70+
71+
```go
72+
// AND - all must be true (short-circuits on first false)
73+
inventory.And(cond1, cond2, cond3)
74+
75+
// OR - any must be true (short-circuits on first true)
76+
inventory.Or(cond1, cond2, cond3)
77+
78+
// NOT - inverts condition
79+
inventory.Not(cond)
80+
```
81+
82+
## Setting Up Context
83+
84+
At request start, set up the context with your actor/request info:
85+
86+
```go
87+
// 1. Set feature flag checker
88+
ctx = inventory.ContextWithFeatureChecker(ctx, func(ctx context.Context, flagName string) (bool, error) {
89+
// Your feature flag service call
90+
return myFeatureFlagService.IsEnabled(ctx, flagName, actor)
91+
})
92+
93+
// 2. Set context bools (pre-computed actor properties)
94+
ctx = inventory.ContextWithBools(ctx, inventory.ContextBools{
95+
"is_cca": actor.IsCCA(),
96+
"is_copilot_chat": actor.IsCopilotChatHost(),
97+
"has_paid_bing": actor.HasPaidBing(),
98+
"is_premium": actor.IsPremium(),
99+
"is_staff": actor.IsStaff(),
100+
})
101+
```
102+
103+
## Common Patterns
104+
105+
### CCA Bypass (most common)
106+
```go
107+
// CCA users always get the tool, others need the feature flag
108+
tool.EnableCondition = inventory.Or(
109+
inventory.ContextBool("is_cca"),
110+
inventory.FeatureFlag("my_feature"),
111+
)
112+
```
113+
114+
### Copilot-chat Host Bypass
115+
```go
116+
tool.EnableCondition = inventory.Or(
117+
inventory.ContextBool("is_copilot_chat"),
118+
inventory.FeatureFlag("my_feature"),
119+
)
120+
```
121+
122+
### Multiple Requirements (AND)
123+
```go
124+
// Requires both premium AND feature flag
125+
tool.EnableCondition = inventory.And(
126+
inventory.ContextBool("is_premium"),
127+
inventory.FeatureFlag("advanced_features"),
128+
)
129+
```
130+
131+
### Kill Switch Pattern
132+
```go
133+
// Enabled by default, but can be killed
134+
tool.EnableCondition = inventory.Not(
135+
inventory.FeatureFlag("kill_my_tool"),
136+
)
137+
```
138+
139+
### Complex Nested Conditions
140+
```go
141+
// Premium users OR (staff with beta flag), but not if kill switch is on
142+
tool.EnableCondition = inventory.And(
143+
inventory.Or(
144+
inventory.ContextBool("is_premium"),
145+
inventory.And(
146+
inventory.ContextBool("is_staff"),
147+
inventory.FeatureFlag("beta_access"),
148+
),
149+
),
150+
inventory.Not(inventory.FeatureFlag("kill_switch")),
151+
)
152+
```
153+
154+
### Custom Logic (fallback)
155+
```go
156+
// For complex cases that can't be expressed with primitives
157+
tool.EnableCondition = inventory.ConditionFunc(func(ctx context.Context) (bool, error) {
158+
actor := actorFromContext(ctx)
159+
return someComplexLogic(actor), nil
160+
})
161+
```
162+
163+
## How Optimization Works
164+
165+
You don't need to do anything special - optimization is automatic:
166+
167+
1. **Build time**: `Builder.Build()` compiles all `EnableCondition`s into bitmask evaluators
168+
2. **Request time**: `AvailableTools(ctx)` builds a `RequestMask` once (evaluates all flags/bools), then uses O(1) bitmask operations per tool
169+
3. **Pre-sorted**: Tools are sorted at build time, so filtering preserves order without re-sorting
170+
171+
### Performance
172+
173+
| Metric | Before | After | Improvement |
174+
|--------|--------|-------|-------------|
175+
| Time (1000 req × 50 tools) | 23.7ms | 12.9ms | **46% faster** |
176+
| Allocations | 15000 | 12000 | 20% fewer |
177+
178+
## Migration Strategy
179+
180+
1. **Phase 1**: Add `EnableCondition` alongside existing `Enabled` func (both work)
181+
2. **Phase 2**: Gradually migrate tools to `EnableCondition`
182+
3. **Phase 3**: Remove old `Enabled` funcs once all migrated
183+
184+
The system is fully backward compatible - `Enabled` func is checked first, then `EnableCondition`.
185+
186+
## Files to Reference
187+
188+
- [conditions.go](../pkg/inventory/conditions.go) - EnableCondition interface and primitives
189+
- [condition_compiler.go](../pkg/inventory/condition_compiler.go) - Bitmask compiler (you don't need to use this directly)
190+
- [conditions_test.go](../pkg/inventory/conditions_test.go) - Usage examples in tests
191+
192+
## Questions?
193+
194+
See PR #1641 for discussion or reach out to @SamMorrowDrums.

0 commit comments

Comments
 (0)