@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 +4 -4
- package/build/index.js +1 -1
- package/build/tools/experiments.js +10 -4
- package/build/tools/features.js +27 -2
- package/build/utils.js +32 -4
- package/package.json +4 -4
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
|
-
|
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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:
|
|
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([
|
|
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:
|
|
246
|
+
enabled: false,
|
|
241
247
|
rules: [
|
|
242
248
|
{
|
|
243
249
|
type: "experiment-ref",
|
package/build/tools/features.js
CHANGED
|
@@ -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
|
|
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("
|
|
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.
|
|
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.
|
|
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.
|
|
31
|
-
"typescript": "^5.
|
|
30
|
+
"@types/node": "^24.2.1",
|
|
31
|
+
"typescript": "^5.9.2"
|
|
32
32
|
},
|
|
33
33
|
"type": "module"
|
|
34
34
|
}
|