@growthbook/mcp 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,12 +2,11 @@
2
2
 
3
3
  With the GrowthBook MCP server, you can interact with GrowthBook right from your LLM client. See experiment details, add a feature flag, and more.
4
4
 
5
- ## Installation
5
+ <a href="https://glama.ai/mcp/servers/@growthbook/growthbook-mcp">
6
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/@growthbook/growthbook-mcp/badge" alt="GrowthBook Server MCP server" />
7
+ </a>
6
8
 
7
- ### Local Installation
8
-
9
- 1. Clone the repo
10
- 2. Run `npx tsc` to generate a build
9
+ ## Setup
11
10
 
12
11
  **Environment Variables**
13
12
  Use the following env variables to configure the MCP server.
@@ -31,8 +30,8 @@ Find instructions below to add the MCP server to a client. Any client that suppo
31
30
  {
32
31
  "mcpServers": {
33
32
  "growthbook": {
34
- "command": "node",
35
- "args": ["ABSOLUTE_PATH_TO_THE_BUILT_MCP_SERVER"],
33
+ "command": "npx",
34
+ "args": ["-y", "@growthbook/mcp"],
36
35
  "env": {
37
36
  "GB_API_KEY": "YOUR_API_KEY",
38
37
  "GB_API_URL": "YOUR_API_URL",
@@ -57,9 +56,9 @@ You should now see a green active status after the server successfully connects!
57
56
  "mcp": {
58
57
  "servers": {
59
58
  "growthbook": {
60
- "command": "node",
59
+ "command": "npx",
61
60
  "args": [
62
- "ABSOLUTE_PATH_TO_THE_BUILT_MCP_SERVER"
61
+ "-y", "@growthbook/mcp"
63
62
  ],
64
63
  "env": {
65
64
  "GB_API_KEY": "YOUR_API_KEY",
@@ -87,8 +86,8 @@ GrowthBook MCP is now ready to use in VS Code.
87
86
  {
88
87
  "mcpServers": {
89
88
  "growthbook": {
90
- "command": "node",
91
- "args": ["ABSOLUTE_PATH_TO_THE_BUILT_MCP_SERVER"],
89
+ "command": "npx",
90
+ "args": ["-y", "@growthbook/mcp"],
92
91
  "env": {
93
92
  "GB_API_KEY": "YOUR_API_KEY",
94
93
  "GB_API_URL": "YOUR_API_URL",
@@ -122,6 +121,7 @@ A hammer icon should appear in the chat window, indicating that your GrowthBook
122
121
  - `get_experiments`: List all experiments in GrowthBook.
123
122
  - `get_experiment`: Fetch details for a specific experiment by ID.
124
123
  - `get_attributes`: List all user attributes tracked in GrowthBook (useful for targeting).
124
+ - `create_experiment`: Creates a feature-flag based experiment.
125
125
 
126
126
  - **Environments**
127
127
 
@@ -137,4 +137,4 @@ A hammer icon should appear in the chat window, indicating that your GrowthBook
137
137
  - `create_sdk_connection`: Create a new SDK connection for your app, specifying language and environment.
138
138
 
139
139
  - **Documentation Search**
140
- - `search_growthbook_docs`: Search the GrowthBook documentation for information on how to use a feature, by keyword or question.
140
+ - `search_growthbook_docs`: Search the GrowthBook documentation for information on how to use a feature, by keyword or question.
package/build/docs.js ADDED
@@ -0,0 +1,508 @@
1
+ export const FEATURE_FLAG_DOCS = {
2
+ react: `## React Feature Flag Implementation
3
+
4
+ ### Boolean Feature Flags
5
+ Use \`useFeatureIsOn\` for simple on/off features. \`useFeatureIsOn\` should appear in the component body, not in the return statement/template:
6
+ \`\`\`tsx
7
+ import { useFeatureIsOn } from "@growthbook/growthbook-react";
8
+
9
+ function LoginPage() {
10
+ const showNewDesign = useFeatureIsOn("new-login-design");
11
+
12
+ return showNewDesign ? <NewLoginForm /> : <OldLoginForm />;
13
+ }
14
+ \`\`\`
15
+
16
+ ### Feature Values with Defaults
17
+ Use \`useFeatureValue\` when you need specific values. \`useFeatureValue\` should appear in the component body, not in the return statement/template:
18
+ \`\`\`tsx
19
+ import { useFeatureValue } from "@growthbook/growthbook-react";
20
+
21
+ function Button() {
22
+ const buttonColor = useFeatureValue("button-color", "blue");
23
+ const buttonSize = useFeatureValue("button-size", "medium");
24
+
25
+ return (
26
+ <button
27
+ className={\`btn btn-\${buttonColor} btn-\${buttonSize}\`}
28
+ >
29
+ Click me
30
+ </button>
31
+ );
32
+ }
33
+ \`\`\`
34
+
35
+ ### Declarative Components
36
+ Use feature components for conditional rendering:
37
+ \`\`\`tsx
38
+ import { IfFeatureEnabled, FeatureString } from "@growthbook/growthbook-react";
39
+
40
+ function HomePage() {
41
+ return (
42
+ <div>
43
+ <h1>
44
+ <FeatureString feature="site-title" default="Welcome" />
45
+ </h1>
46
+
47
+ <IfFeatureEnabled feature="promo-banner">
48
+ <PromoBanner />
49
+ </IfFeatureEnabled>
50
+
51
+ <IfFeatureEnabled feature="new-checkout" fallback={<OldCheckout />}>
52
+ <NewCheckout />
53
+ </IfFeatureEnabled>
54
+ </div>
55
+ );
56
+ }
57
+ \`\`\``,
58
+ javascript: `## JavaScript Feature Flag Implementation
59
+
60
+ ### Simple Feature Checks
61
+ Use \`isOn()\` for boolean features:
62
+ \`\`\`javascript
63
+ function renderDashboard() {
64
+ if (gb.isOn("new-dashboard")) {
65
+ return renderNewDashboard();
66
+ }
67
+ return renderOldDashboard();
68
+ }
69
+ \`\`\`
70
+
71
+ ### Feature Values with Fallbacks
72
+ Use \`getFeatureValue()\` for configuration values:
73
+ \`\`\`javascript
74
+ function configureAPI() {
75
+ const apiTimeout = gb.getFeatureValue("api-timeout", 5000);
76
+ const retryCount = gb.getFeatureValue("retry-count", 3);
77
+
78
+ return {
79
+ timeout: apiTimeout,
80
+ retries: retryCount
81
+ };
82
+ }
83
+ \`\`\`
84
+
85
+ ### Detailed Feature Evaluation
86
+ Use \`evalFeature()\` when you need metadata:
87
+ \`\`\`javascript
88
+ function trackFeatureUsage(featureKey) {
89
+ const result = gb.evalFeature(featureKey);
90
+
91
+ // Track feature usage for analytics
92
+ analytics.track('feature_evaluated', {
93
+ feature: featureKey,
94
+ value: result.value,
95
+ source: result.source,
96
+ ruleId: result.ruleId
97
+ });
98
+
99
+ return result.value;
100
+ }
101
+ \`\`\``,
102
+ vue: `## Vue Feature Flag Implementation
103
+
104
+ ### Composition API
105
+ \`\`\`typescript
106
+ import { ref, watch, inject } from 'vue'
107
+
108
+ const showBanner = ref(growthbook?.isOn('show-banner'))
109
+
110
+ // Optional real-time watching
111
+ watch(growthbook, () => {
112
+ showBanner.value = growthbook?.isOn('show-banner')
113
+ })
114
+ \`\`\`
115
+
116
+ ### Options API
117
+ \`\`\`typescript
118
+ export default {
119
+ data() {
120
+ return {
121
+ showBanner: false
122
+ }
123
+ },
124
+ mounted() {
125
+ const gb = inject(gbKey)
126
+ if (gb) {
127
+ this.showBanner = gb.isOn('show-banner')
128
+ }
129
+ }
130
+ }
131
+ \`\`\`
132
+
133
+ ### Template Usage
134
+ \`\`\`html
135
+ <template>
136
+ <div>
137
+ <h1 v-if="showBanner">Now you see me!</h1>
138
+ </div>
139
+ </template>
140
+ \`\`\``,
141
+ python: `## Python Feature Flag Implementation
142
+
143
+ ### Boolean Feature Checks
144
+ Use \`is_on()\` and \`is_off()\` for feature toggles:
145
+ \`\`\`python
146
+ def process_payment(gb, payment_data):
147
+ if gb.is_on("new-payment-processor"):
148
+ return new_payment_service.process(payment_data)
149
+ else:
150
+ return legacy_payment_service.process(payment_data)
151
+ \`\`\`
152
+
153
+ ### Feature Values with Defaults
154
+ Use \`get_feature_value()\` for configuration:
155
+ \`\`\`python
156
+ def get_api_config(gb):
157
+ return {
158
+ 'timeout': gb.get_feature_value("api-timeout", 30),
159
+ 'max_retries': gb.get_feature_value("max-retries", 3),
160
+ 'batch_size': gb.get_feature_value("batch-size", 100)
161
+ }
162
+ \`\`\`
163
+
164
+ ### Detailed Feature Evaluation
165
+ Use \`evalFeature()\` for comprehensive feature info:
166
+ \`\`\`python
167
+ def evaluate_feature_with_tracking(gb, feature_key):
168
+ result = gb.evalFeature(feature_key)
169
+
170
+ # Log feature evaluation for debugging
171
+ logger.info(f"Feature {feature_key}: value={result.value}, source={result.source}")
172
+
173
+ return result.value
174
+ \`\`\`
175
+
176
+ ### Web Framework Integration (Django Example)
177
+ \`\`\`python
178
+ def growthbook_middleware(get_response):
179
+ def middleware(request):
180
+ # Initialize GrowthBook for each request
181
+ request.gb = create_growthbook_instance(
182
+ user_id=getattr(request.user, 'id', None),
183
+ is_authenticated=request.user.is_authenticated,
184
+ user_type=getattr(request.user, 'user_type', 'anonymous')
185
+ )
186
+
187
+ response = get_response(request)
188
+ return response
189
+ return middleware
190
+ \`\`\``,
191
+ go: `## Go Feature Flag Implementation
192
+
193
+ ### Basic Feature Evaluation
194
+ \`\`\`go
195
+ // Evaluate a text feature
196
+ buttonColor := client.EvalFeature(context.Background(), "buy-button-color")
197
+ if buttonColor.Value == "blue" {
198
+ // Perform actions for blue button
199
+ }
200
+
201
+ // Evaluate a boolean feature
202
+ darkMode := client.EvalFeature(context.Background(), "dark-mode")
203
+ if darkMode.On {
204
+ // Enable dark mode
205
+ }
206
+ \`\`\`
207
+
208
+ ### Detailed Feature Inspection
209
+ \`\`\`go
210
+ result, err := client.EvalFeature(context.TODO(), "my-feature")
211
+ if err != nil {
212
+ // Handle error
213
+ }
214
+
215
+ // Check feature value and source
216
+ if result.On {
217
+ // Feature is enabled
218
+ }
219
+
220
+ // Inspect how the feature value was determined
221
+ switch result.Source {
222
+ case gb.DefaultValueResultSource:
223
+ // Used default value
224
+ case gb.ExperimentResultSource:
225
+ // Value determined by an experiment
226
+ case gb.ForceResultSource:
227
+ // Manually forced value
228
+ }
229
+ \`\`\``,
230
+ php: `## PHP Feature Flag Implementation
231
+
232
+ ### Basic Feature Checks
233
+ \`\`\`php
234
+ // Check if a feature is on
235
+ if ($growthbook->isOn("my-feature")) {
236
+ echo "It's on!";
237
+ }
238
+
239
+ // Check if a feature is off
240
+ if ($growthbook->isOff("my-feature")) {
241
+ echo "It's off :(";
242
+ }
243
+ \`\`\`
244
+
245
+ ### Feature Values with Fallback
246
+ \`\`\`php
247
+ // Get feature value with default fallback
248
+ $color = $growthbook->getValue("button-color", "blue");
249
+ echo "<button style='color:\${color}'>Click Me!</button>";
250
+ \`\`\`
251
+
252
+ ### Detailed Feature Result
253
+ \`\`\`php
254
+ $featureResult = $growthbook->getFeature("my-feature");
255
+
256
+ // Access feature result properties
257
+ $value = $featureResult->value;
258
+ $isOn = $featureResult->on;
259
+ $source = $featureResult->source; // e.g. 'defaultValue', 'experiment'
260
+ \`\`\`
261
+
262
+ ### Inline Experiments
263
+ \`\`\`php
264
+ $exp = Growthbook\\InlineExperiment::create(
265
+ "my-experiment",
266
+ ["red", "blue", "green"]
267
+ );
268
+
269
+ // Run experiment and get result
270
+ $result = $growthbook->runInlineExperiment($exp);
271
+ echo $result->value; // Will be "red", "blue", or "green"
272
+ \`\`\``,
273
+ ruby: `## Ruby Feature Flag Implementation
274
+
275
+ ### Basic Feature Evaluation
276
+ \`\`\`ruby
277
+ # Check if feature is enabled
278
+ if gb.feature_on?("my-feature")
279
+ puts "Feature is on!"
280
+ end
281
+
282
+ # Get feature value with default
283
+ color = gb.get_feature_value("button-color", "blue")
284
+ \`\`\`
285
+
286
+ ### Detailed Feature Results
287
+ \`\`\`ruby
288
+ result = gb.eval_feature("my-feature")
289
+ puts result.value
290
+ puts result.on?
291
+ puts result.source
292
+ \`\`\``,
293
+ java: `## Java Feature Flag Implementation
294
+
295
+ ### Feature State Checks
296
+ \`\`\`java
297
+ // Check if a feature is on/off
298
+ boolean isDarkModeEnabled = growthBook.isOn("dark_mode");
299
+ boolean isFeatureDisabled = growthBook.isOff("feature_key");
300
+ \`\`\`
301
+
302
+ ### Feature Values with Defaults
303
+ \`\`\`java
304
+ // Get feature value with a default
305
+ String welcomeMessage = growthBook.getFeatureValue("welcome_message", "Default Welcome");
306
+ Float pricing = growthBook.getFeatureValue("product_price", 9.99f);
307
+ \`\`\`
308
+
309
+ ### Complex Type Evaluation
310
+ \`\`\`java
311
+ // For complex objects, specify the class for deserialization
312
+ MyConfig config = growthBook.getFeatureValue(
313
+ "app_config",
314
+ defaultConfig,
315
+ MyConfig.class
316
+ );
317
+
318
+ // Detailed feature evaluation
319
+ FeatureResult<Float> result = growthBook.evalFeature("pricing");
320
+ Float value = result.getValue();
321
+ FeatureResultSource source = result.getSource();
322
+ \`\`\``,
323
+ csharp: `## C# Feature Flag Implementation
324
+
325
+ ### Basic Feature Evaluation
326
+ \`\`\`csharp
327
+ // Check if feature is enabled
328
+ if (gb.IsOn("my-feature"))
329
+ {
330
+ Console.WriteLine("Feature is enabled!");
331
+ }
332
+
333
+ // Get feature value with default
334
+ var buttonColor = gb.GetFeatureValue("button-color", "blue");
335
+ \`\`\`
336
+
337
+ ### Typed Feature Values
338
+ \`\`\`csharp
339
+ // Get strongly typed feature values
340
+ var maxRetries = gb.GetFeatureValue<int>("max-retries", 3);
341
+ var timeout = gb.GetFeatureValue<double>("timeout-seconds", 30.0);
342
+ \`\`\`
343
+
344
+ ### Feature Result Details
345
+ \`\`\`csharp
346
+ var result = gb.EvalFeature("my-feature");
347
+ Console.WriteLine($"Value: {result.Value}");
348
+ Console.WriteLine($"Source: {result.Source}");
349
+ Console.WriteLine($"On: {result.On}");
350
+ \`\`\``,
351
+ swift: `## Swift Feature Flag Implementation
352
+
353
+ ### Basic Feature Checks
354
+ \`\`\`swift
355
+ // Check if feature is enabled
356
+ if gb.isOn(feature: "dark-mode") {
357
+ // Enable dark mode
358
+ }
359
+
360
+ // Get feature value with default
361
+ let buttonColor = gb.getFeatureValue(feature: "button-color", defaultValue: "blue")
362
+ \`\`\`
363
+
364
+ ### Feature Evaluation
365
+ \`\`\`swift
366
+ let result = gb.evalFeature("my-feature")
367
+ print("Value: \\(result.value)")
368
+ print("On: \\(result.on)")
369
+ print("Source: \\(result.source)")
370
+ \`\`\``,
371
+ elixir: `## Elixir Feature Flag Implementation
372
+
373
+ ### Basic Feature Evaluation
374
+ \`\`\`elixir
375
+ # Check if feature is enabled
376
+ case GrowthBook.feature_on?(gb, "my-feature") do
377
+ true -> IO.puts("Feature is on!")
378
+ false -> IO.puts("Feature is off")
379
+ end
380
+
381
+ # Get feature value with default
382
+ button_color = GrowthBook.get_feature_value(gb, "button-color", "blue")
383
+ \`\`\`
384
+
385
+ ### Feature Result Evaluation
386
+ \`\`\`elixir
387
+ result = GrowthBook.eval_feature(gb, "my-feature")
388
+ IO.inspect(result.value)
389
+ IO.inspect(result.on)
390
+ IO.inspect(result.source)
391
+ \`\`\``,
392
+ kotlin: `## Kotlin Feature Flag Implementation
393
+
394
+ ### Basic Feature Checks
395
+ \`\`\`kotlin
396
+ // Get feature and check if enabled
397
+ val feature = gb.feature("dark-mode")
398
+ if (feature.on) {
399
+ // Enable dark mode
400
+ }
401
+
402
+ // Check if feature is off
403
+ if (feature.off) {
404
+ // Feature is disabled
405
+ }
406
+ \`\`\`
407
+
408
+ ### Feature Values
409
+ \`\`\`kotlin
410
+ // Get feature value
411
+ val buttonColor = gb.feature("button-color")
412
+ val colorValue = buttonColor.value
413
+
414
+ // Direct value access with fallback logic
415
+ val actualColor = if (buttonColor.on) buttonColor.value else "blue"
416
+ \`\`\`
417
+
418
+ ### Feature Result Details
419
+ \`\`\`kotlin
420
+ val feature = gb.feature("my-feature")
421
+ println("Value: \${feature.value}")
422
+ println("On: \${feature.on}")
423
+ println("Off: \${feature.off}")
424
+ println("Source: \${feature.source}")
425
+ \`\`\``,
426
+ flutter: `## Flutter Feature Flag Implementation
427
+
428
+ ### Basic Feature Usage
429
+ \`\`\`dart
430
+ // Check if feature is enabled
431
+ if (gb.isOn('dark-mode')) {
432
+ // Enable dark mode
433
+ }
434
+
435
+ // Get feature value with default
436
+ String buttonColor = gb.getFeatureValue('button-color', 'blue');
437
+ \`\`\`
438
+
439
+ ### Widget Integration
440
+ \`\`\`dart
441
+ class MyWidget extends StatelessWidget {
442
+ @override
443
+ Widget build(BuildContext context) {
444
+ final showBanner = gb.isOn('show-banner');
445
+
446
+ return Column(
447
+ children: [
448
+ if (showBanner) BannerWidget(),
449
+ MainContent(),
450
+ ],
451
+ );
452
+ }
453
+ }
454
+ \`\`\`
455
+
456
+ ### Feature Result Evaluation
457
+ \`\`\`dart
458
+ GBFeatureResult result = gb.evalFeature('my-feature');
459
+ print('Value: \${result.value}');
460
+ print('On: \${result.on}');
461
+ print('Source: \${result.source}');
462
+ \`\`\``,
463
+ };
464
+ export function getFeatureFlagDocs(language) {
465
+ switch (language.toLowerCase()) {
466
+ case "react":
467
+ case "tsx":
468
+ case "jsx":
469
+ return FEATURE_FLAG_DOCS.react;
470
+ case "javascript":
471
+ case "js":
472
+ case "node":
473
+ return FEATURE_FLAG_DOCS.javascript;
474
+ case "vue":
475
+ return FEATURE_FLAG_DOCS.vue;
476
+ case "python":
477
+ case "py":
478
+ return FEATURE_FLAG_DOCS.python;
479
+ case "go":
480
+ return FEATURE_FLAG_DOCS.go;
481
+ case "php":
482
+ return FEATURE_FLAG_DOCS.php;
483
+ case "ruby":
484
+ case "rb":
485
+ return FEATURE_FLAG_DOCS.ruby;
486
+ case "java":
487
+ return FEATURE_FLAG_DOCS.java;
488
+ case "csharp":
489
+ case "cs":
490
+ return FEATURE_FLAG_DOCS.csharp;
491
+ case "swift":
492
+ return FEATURE_FLAG_DOCS.swift;
493
+ case "elixir":
494
+ case "ex":
495
+ case "exs":
496
+ return FEATURE_FLAG_DOCS.elixir;
497
+ case "kotlin":
498
+ case "kt":
499
+ case "kts":
500
+ case "ktm":
501
+ return FEATURE_FLAG_DOCS.kotlin;
502
+ case "flutter":
503
+ case "dart":
504
+ return FEATURE_FLAG_DOCS.flutter;
505
+ default:
506
+ return "Feature flag documentation not available for this language. Check GrowthBook docs for implementation details.";
507
+ }
508
+ }
package/build/index.js CHANGED
@@ -7,7 +7,8 @@ import { registerFeatureTools } from "./tools/features.js";
7
7
  import { registerProjectTools } from "./tools/projects.js";
8
8
  import { registerSdkConnectionTools } from "./tools/sdk-connections.js";
9
9
  import { getApiKey, getApiUrl, getAppOrigin, getUser } from "./utils.js";
10
- import { registerSearchTool } from "./tools/search.js";
10
+ import { registerSearchTools } from "./tools/search.js";
11
+ import { registerDefaultsTools } from "./tools/defaults.js";
11
12
  export const baseApiUrl = getApiUrl();
12
13
  export const apiKey = getApiKey();
13
14
  export const appOrigin = getAppOrigin();
@@ -17,7 +18,34 @@ const server = new McpServer({
17
18
  name: "GrowthBook MCP",
18
19
  version: "1.0.0",
19
20
  }, {
20
- instructions: "You are a helpful assistant that interacts with GrowthBook, an open source feature flagging and experimentation platform. You can use tools to create and manage feature flags, experiments, and environments. Note that experiments are also called a/b tests.",
21
+ instructions: `You are a helpful assistant that interacts with GrowthBook, an open source feature flagging and experimentation platform. You can create and manage feature flags, experiments (A/B tests), and other resources associated with GrowthBook.
22
+
23
+ **Key Workflows:**
24
+
25
+ 1. **Creating Feature Flags:**
26
+ - Use create_feature_flag for simple boolean/string/number/json flags
27
+ - Use create_force_rule to add conditional rules to existing flags
28
+ - Always specify the correct fileExtension for code integration
29
+
30
+ 2. **Creating Experiments (A/B Tests):**
31
+ - CRITICAL: Always call get_defaults FIRST to see naming conventions and examples
32
+ - Use create_experiment to create experiments
33
+ - Experiments automatically create linked feature flags
34
+
35
+ 3. **Exploring Existing Resources:**
36
+ - Use get_projects, get_environments, get_feature_flags, get_experiments, or get_attributes to understand current setup
37
+ - Use get_single_feature_flag for detailed flag information
38
+ - Use get_stale_safe_rollouts to find completed rollouts that can be cleaned up
39
+
40
+ 4. **SDK Integration:**
41
+ - Use get_sdk_connections to see existing integrations
42
+ - Use create_sdk_connection for new app integrations
43
+ - Use generate_flag_types to create TypeScript definitions
44
+
45
+ **Important Notes:**
46
+ - Feature flags and experiments require a fileExtension parameter for proper code integration
47
+ - Always review generated GrowthBook links with users so they can launch experiments
48
+ - When experiments are "draft", users must visit GrowthBook to review and launch them`,
21
49
  });
22
50
  registerEnvironmentTools({
23
51
  server,
@@ -46,9 +74,15 @@ registerExperimentTools({
46
74
  baseApiUrl,
47
75
  apiKey,
48
76
  appOrigin,
77
+ user,
78
+ });
79
+ registerSearchTools({
80
+ server,
49
81
  });
50
- registerSearchTool({
82
+ registerDefaultsTools({
51
83
  server,
84
+ baseApiUrl,
85
+ apiKey,
52
86
  });
53
87
  // Start receiving messages on stdin and sending messages on stdout
54
88
  const transport = new StdioServerTransport();