@growthbook/mcp 0.1.3 → 1.0.0

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
@@ -14,7 +14,7 @@ Use the following env variables to configure the MCP server.
14
14
  | Variable Name | Status | Description |
15
15
  | ------------- | -------- | ----------------------------------------------------------------- |
16
16
  | GB_API_KEY | Required | A GrowthBook API key. |
17
- | GB_USER | Required | Your name. Used when creating a feature flag. |
17
+ | GB_EMAIL | Required | Your email address used with GrowthBook. Used when creating feature flags and experiments.|
18
18
  | GB_API_URL | Optional | Your GrowthBook API URL. Defaults to `https://api.growthbook.io`. |
19
19
  | GB_APP_ORIGIN | Optional | Your GrowthBook app URL Defaults to `https://app.growthbook.io`. |
20
20
 
@@ -36,7 +36,7 @@ Find instructions below to add the MCP server to a client. Any client that suppo
36
36
  "GB_API_KEY": "YOUR_API_KEY",
37
37
  "GB_API_URL": "YOUR_API_URL",
38
38
  "GB_APP_ORIGIN": "YOUR_APP_ORIGIN",
39
- "GB_USER": "YOUR_NAME"
39
+ "GB_EMAIL": "YOUR_EMAIL"
40
40
  }
41
41
  }
42
42
  }
@@ -64,7 +64,7 @@ You should now see a green active status after the server successfully connects!
64
64
  "GB_API_KEY": "YOUR_API_KEY",
65
65
  "GB_API_URL": "YOUR_API_URL",
66
66
  "GB_APP_ORIGIN": "YOUR_APP_ORIGIN",
67
- "GB_USER": "YOUR_NAME"
67
+ "GB_EMAIL": "YOUR_EMAIL"
68
68
  }
69
69
  }
70
70
  }
@@ -92,7 +92,7 @@ GrowthBook MCP is now ready to use in VS Code.
92
92
  "GB_API_KEY": "YOUR_API_KEY",
93
93
  "GB_API_URL": "YOUR_API_URL",
94
94
  "GB_APP_ORIGIN": "YOUR_APP_ORIGIN",
95
- "GB_USER": "YOUR_NAME"
95
+ "GB_EMAIL": "YOUR_EMAIL"
96
96
  }
97
97
  }
98
98
  }
package/build/index.js CHANGED
@@ -12,7 +12,7 @@ import { registerDefaultsTools } from "./tools/defaults.js";
12
12
  export const baseApiUrl = getApiUrl();
13
13
  export const apiKey = getApiKey();
14
14
  export const appOrigin = getAppOrigin();
15
- export const user = getUser();
15
+ export const user = await getUser(baseApiUrl, apiKey);
16
16
  // Create an MCP server
17
17
  const server = new McpServer({
18
18
  name: "GrowthBook MCP",
@@ -57,7 +57,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
57
57
  // Loop through the environments and create a rule for each one keyed by environment name
58
58
  environments: defaultEnvironments.reduce((acc, env) => {
59
59
  acc[env] = {
60
- enabled: true,
60
+ enabled: false,
61
61
  rules: [
62
62
  {
63
63
  type: "force",
@@ -176,7 +176,12 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
176
176
  .string()
177
177
  .describe("Variation name. Base name off the examples from get_defaults. If none are available, use a short, descriptive name that captures the essence of the variation."),
178
178
  value: z
179
- .union([z.string(), z.number(), z.boolean(), z.record(z.any())])
179
+ .union([
180
+ z.string(),
181
+ z.number(),
182
+ z.boolean(),
183
+ z.record(z.string(), z.any()),
184
+ ])
180
185
  .describe("The value of the control and each of the variations. The value should be a string, number, boolean, or object. If it's an object, it should be a valid JSON object."),
181
186
  }))
182
187
  .describe("Experiment variations. The key should be the variation name and the value should be the variation value. Look to variations included in preview experiments for guidance on generation. The default or control variation should always be first."),
@@ -203,6 +208,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
203
208
  name,
204
209
  description,
205
210
  hypothesis,
211
+ owner: user.email,
206
212
  trackingKey: name.toLowerCase().replace(/[^a-z0-9]/g, "-"),
207
213
  tags: ["mcp"],
208
214
  assignmentQueryId: experimentDefaults?.assignmentQuery,
@@ -226,7 +232,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
226
232
  const flagId = `flag_${name.toLowerCase().replace(/[^a-z0-9]/g, "_")}`;
227
233
  const flagPayload = {
228
234
  id: flagId,
229
- owner: user,
235
+ owner: user.name,
230
236
  defaultValue: variations[0].value,
231
237
  valueType: typeof variations[0].value === "string"
232
238
  ? "string"
@@ -237,7 +243,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
237
243
  environments: {
238
244
  ...experimentDefaults.environments.reduce((acc, env) => {
239
245
  acc[env] = {
240
- enabled: true,
246
+ enabled: false,
241
247
  rules: [
242
248
  {
243
249
  type: "experiment-ref",
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { getDocsMetadata, handleResNotOk, generateLinkToGrowthBook, SUPPORTED_FILE_EXTENSIONS, } from "../utils.js";
3
3
  import { exec } from "child_process";
4
+ import { getDefaults } from "./defaults.js";
4
5
  export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, user, }) {
5
6
  /**
6
7
  * Tool: create_feature_flag
@@ -14,7 +15,7 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
14
15
  .string()
15
16
  .optional()
16
17
  .default("")
17
- .describe("A briefdescription of the feature flag"),
18
+ .describe("A brief description of the feature flag"),
18
19
  valueType: z
19
20
  .enum(["string", "number", "boolean", "json"])
20
21
  .describe("The value type the feature flag will return"),
@@ -25,13 +26,37 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
25
26
  .enum(SUPPORTED_FILE_EXTENSIONS)
26
27
  .describe("The extension of the current file. If it's unclear, ask the user."),
27
28
  }, async ({ id, description, valueType, defaultValue, fileExtension }) => {
29
+ // get environments
30
+ let environments = [];
31
+ const defaults = await getDefaults(apiKey, baseApiUrl);
32
+ if (defaults.environments) {
33
+ environments = defaults.environments;
34
+ }
35
+ else {
36
+ const envRes = await fetch(`${baseApiUrl}/api/v1/features/environments`, {
37
+ headers: {
38
+ Authorization: `Bearer ${apiKey}`,
39
+ "Content-Type": "application/json",
40
+ },
41
+ });
42
+ await handleResNotOk(envRes);
43
+ const envData = await envRes.json();
44
+ environments = envData.environments.map((env) => env.id);
45
+ }
28
46
  const payload = {
29
47
  id,
30
48
  description,
31
- owner: user,
49
+ owner: user.name,
32
50
  valueType,
33
51
  defaultValue,
34
52
  tags: ["mcp"],
53
+ environments: environments.reduce((acc, env) => {
54
+ acc[env] = {
55
+ enabled: false,
56
+ rules: [],
57
+ };
58
+ return acc;
59
+ }, {}),
35
60
  };
36
61
  try {
37
62
  const res = await fetch(`${baseApiUrl}/api/v1/features`, {
package/build/utils.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { z } from "zod";
1
2
  import { getFeatureFlagDocs } from "./docs.js";
2
3
  // Shared file extension enum for all MCP tools
3
4
  export const SUPPORTED_FILE_EXTENSIONS = [
@@ -52,12 +53,39 @@ export function getAppOrigin() {
52
53
  const userAppOrigin = process.env.GB_APP_ORIGIN;
53
54
  return `${userAppOrigin || defaultAppOrigin}`;
54
55
  }
55
- export function getUser() {
56
- const user = process.env.GB_USER;
56
+ export async function getUser(baseApiUrl, apiKey) {
57
+ const user = process.env.GB_EMAIL || process.env.GB_USER;
57
58
  if (!user) {
58
- throw new Error("GB_USER environment variable is required");
59
+ throw new Error("GB_EMAIL environment variable is required");
60
+ }
61
+ // Show deprecation warning if using the old variable
62
+ if (process.env.GB_USER && !process.env.GB_EMAIL) {
63
+ console.error("⚠️ GB_USER is deprecated. Use GB_EMAIL instead.");
64
+ }
65
+ const emailSchema = z.string().email();
66
+ if (!emailSchema.safeParse(user).success) {
67
+ throw new Error("GB_EMAIL is not a valid email");
68
+ }
69
+ try {
70
+ const users = await fetch(`${baseApiUrl}/api/v1/members?userEmail=${user}`, {
71
+ headers: {
72
+ Authorization: `Bearer ${apiKey}`,
73
+ },
74
+ });
75
+ await handleResNotOk(users);
76
+ const usersData = await users.json();
77
+ if (usersData.members.length === 0) {
78
+ throw new Error(`Email not found in GrowthBook. Update GB_EMAIL environment variable to your email address in GrowthBook.`);
79
+ }
80
+ const userFromGrowthBook = {
81
+ email: usersData.members[0].email,
82
+ name: usersData.members[0].name,
83
+ };
84
+ return userFromGrowthBook;
85
+ }
86
+ catch (error) {
87
+ throw new Error(`Error fetching user from GrowthBook. Please check your GB_EMAIL and GB_API_KEY environment variables.`);
59
88
  }
60
- return user;
61
89
  }
62
90
  export function getDocsMetadata(extension) {
63
91
  switch (extension) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@growthbook/mcp",
3
- "version": "0.1.3",
3
+ "version": "1.0.0",
4
4
  "description": "",
5
5
  "access": "public",
6
6
  "homepage": "https://github.com/growthbook/growthbook-mcp",
@@ -22,13 +22,13 @@
22
22
  "license": "MIT",
23
23
  "packageManager": "pnpm@10.6.1",
24
24
  "dependencies": {
25
- "@modelcontextprotocol/sdk": "^1.13.1",
25
+ "@modelcontextprotocol/sdk": "^1.17.2",
26
26
  "env-paths": "^3.0.0",
27
27
  "zod": "^3.25.67"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/node": "^24.0.4",
31
- "typescript": "^5.8.3"
30
+ "@types/node": "^24.2.1",
31
+ "typescript": "^5.9.2"
32
32
  },
33
33
  "type": "module"
34
34
  }