@growthbook/mcp 1.0.2 → 1.1.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 +1 -122
- package/build/index.js +8 -0
- package/build/tools/experiments.js +45 -13
- package/build/tools/features.js +2 -3
- package/build/tools/metrics.js +87 -0
- package/build/tools/projects.js +2 -4
- package/build/tools/sdk-connections.js +2 -3
- package/build/utils.js +19 -0
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -18,126 +18,5 @@ Use the following env variables to configure the MCP server.
|
|
|
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
|
|
|
21
|
-
Find instructions below to add the MCP server to a client. Any client that supports MCP is also compatible. Consult its documentation for how to add the server.
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
1. Open **Cursor Settings** → **MCP**
|
|
26
|
-
2. Click **Add new global MCP server**
|
|
27
|
-
3. Add an entry for the GrowthBook MCP, following the pattern below:
|
|
28
|
-
|
|
29
|
-
```json
|
|
30
|
-
{
|
|
31
|
-
"mcpServers": {
|
|
32
|
-
"growthbook": {
|
|
33
|
-
"command": "npx",
|
|
34
|
-
"args": ["-y", "@growthbook/mcp"],
|
|
35
|
-
"env": {
|
|
36
|
-
"GB_API_KEY": "YOUR_API_KEY",
|
|
37
|
-
"GB_API_URL": "YOUR_API_URL",
|
|
38
|
-
"GB_APP_ORIGIN": "YOUR_APP_ORIGIN",
|
|
39
|
-
"GB_EMAIL": "YOUR_EMAIL"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
3. Save the settings.
|
|
47
|
-
|
|
48
|
-
You should now see a green active status after the server successfully connects!
|
|
49
|
-
|
|
50
|
-
### VS Code
|
|
51
|
-
|
|
52
|
-
1. Open **User Settings (JSON)**
|
|
53
|
-
2. Add an MCP entry:
|
|
54
|
-
|
|
55
|
-
```json
|
|
56
|
-
"mcp": {
|
|
57
|
-
"servers": {
|
|
58
|
-
"growthbook": {
|
|
59
|
-
"command": "npx",
|
|
60
|
-
"args": [
|
|
61
|
-
"-y", "@growthbook/mcp"
|
|
62
|
-
],
|
|
63
|
-
"env": {
|
|
64
|
-
"GB_API_KEY": "YOUR_API_KEY",
|
|
65
|
-
"GB_API_URL": "YOUR_API_URL",
|
|
66
|
-
"GB_APP_ORIGIN": "YOUR_APP_ORIGIN",
|
|
67
|
-
"GB_EMAIL": "YOUR_EMAIL"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
3. Save your settings.
|
|
75
|
-
|
|
76
|
-
GrowthBook MCP is now ready to use in VS Code.
|
|
77
|
-
|
|
78
|
-
### Claude Desktop
|
|
79
|
-
|
|
80
|
-
1. **Open Settings** → **Developer**
|
|
81
|
-
2. Click **Edit Config**
|
|
82
|
-
3. Open `claude_desktop_config.json`
|
|
83
|
-
4. Add the following configuration:
|
|
84
|
-
|
|
85
|
-
```json
|
|
86
|
-
{
|
|
87
|
-
"mcpServers": {
|
|
88
|
-
"growthbook": {
|
|
89
|
-
"command": "npx",
|
|
90
|
-
"args": ["-y", "@growthbook/mcp"],
|
|
91
|
-
"env": {
|
|
92
|
-
"GB_API_KEY": "YOUR_API_KEY",
|
|
93
|
-
"GB_API_URL": "YOUR_API_URL",
|
|
94
|
-
"GB_APP_ORIGIN": "YOUR_APP_ORIGIN",
|
|
95
|
-
"GB_EMAIL": "YOUR_EMAIL"
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
5. Save the config and restart Claude
|
|
103
|
-
|
|
104
|
-
A hammer icon should appear in the chat window, indicating that your GrowthBook MCP server is connected and available!
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## Tools
|
|
109
|
-
|
|
110
|
-
- **Feature Flags**
|
|
111
|
-
|
|
112
|
-
- `create_feature_flag`: Create, add, or wrap an element with a feature flag. Specify key, type, default value, and metadata.
|
|
113
|
-
- `get_feature_flags`: List all feature flags in your GrowthBook instance.
|
|
114
|
-
- `get_single_feature_flag`: Fetch details for a specific feature flag by ID.
|
|
115
|
-
- `get_stale_safe_rollouts`: List all safe rollout rules that have been rolled back or released.
|
|
116
|
-
- `create_force_rule`: Create a feature flag with a targeting condition.
|
|
117
|
-
- `generate_flag_types`: Generates types for feature flags
|
|
118
|
-
|
|
119
|
-
- **Experiments**
|
|
120
|
-
|
|
121
|
-
- `get_experiments`: List all experiments in GrowthBook.
|
|
122
|
-
- `get_experiment`: Fetch details for a specific experiment by ID.
|
|
123
|
-
- `get_attributes`: List all user attributes tracked in GrowthBook (useful for targeting).
|
|
124
|
-
- `create_experiment`: Creates a feature-flag based experiment.
|
|
125
|
-
- `get_defaults`: Get default values for experiments including hypothesis, description, datasource, and assignment query. (Runs automatically when the create experiment tool is called.)
|
|
126
|
-
- `create_defaults`: Set custom default values for experiments that will be used when creating new experiments.
|
|
127
|
-
- `clear_user_defaults`: Clear user-defined defaults and revert to automatic defaults.
|
|
128
|
-
|
|
129
|
-
- **Environments**
|
|
130
|
-
|
|
131
|
-
- `get_environments`: List all environments (e.g., production, staging) configured in GrowthBook.
|
|
132
|
-
|
|
133
|
-
- **Projects**
|
|
134
|
-
|
|
135
|
-
- `get_projects`: List all projects in your GrowthBook instance.
|
|
136
|
-
|
|
137
|
-
- **SDK Connections**
|
|
138
|
-
|
|
139
|
-
- `get_sdk_connections`: List all SDK connections (how GrowthBook connects to your apps).
|
|
140
|
-
- `create_sdk_connection`: Create a new SDK connection for your app, specifying language and environment.
|
|
141
|
-
|
|
142
|
-
- **Documentation Search**
|
|
143
|
-
- `search_growthbook_docs`: Search the GrowthBook documentation for information on how to use a feature, by keyword or question.
|
|
22
|
+
Add the MCP server to your AI tool of choice. See the [official docs](https://docs.growthbook.io/integrations/mcp) for complete a complete guide.
|
package/build/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { registerSdkConnectionTools } from "./tools/sdk-connections.js";
|
|
|
9
9
|
import { getApiKey, getApiUrl, getAppOrigin } from "./utils.js";
|
|
10
10
|
import { registerSearchTools } from "./tools/search.js";
|
|
11
11
|
import { registerDefaultsTools } from "./tools/defaults.js";
|
|
12
|
+
import { registerMetricsTools } from "./tools/metrics.js";
|
|
12
13
|
export const baseApiUrl = getApiUrl();
|
|
13
14
|
export const apiKey = getApiKey();
|
|
14
15
|
export const appOrigin = getAppOrigin();
|
|
@@ -87,6 +88,13 @@ registerDefaultsTools({
|
|
|
87
88
|
baseApiUrl,
|
|
88
89
|
apiKey,
|
|
89
90
|
});
|
|
91
|
+
registerMetricsTools({
|
|
92
|
+
server,
|
|
93
|
+
baseApiUrl,
|
|
94
|
+
apiKey,
|
|
95
|
+
appOrigin,
|
|
96
|
+
user,
|
|
97
|
+
});
|
|
90
98
|
// Start receiving messages on stdin and sending messages on stdout
|
|
91
99
|
const transport = new StdioServerTransport();
|
|
92
100
|
await server.connect(transport);
|
|
@@ -1,29 +1,61 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { generateLinkToGrowthBook, getDocsMetadata, handleResNotOk, SUPPORTED_FILE_EXTENSIONS, } from "../utils.js";
|
|
2
|
+
import { generateLinkToGrowthBook, getDocsMetadata, handleResNotOk, SUPPORTED_FILE_EXTENSIONS, paginationSchema, } from "../utils.js";
|
|
3
3
|
import { getDefaults } from "./defaults.js";
|
|
4
4
|
export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin, user, }) {
|
|
5
5
|
/**
|
|
6
6
|
* Tool: get_experiments
|
|
7
7
|
*/
|
|
8
|
-
server.tool("get_experiments", "Fetches
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}, async ({ limit, offset }) => {
|
|
8
|
+
server.tool("get_experiments", "Fetches experiments from the GrowthBook API", {
|
|
9
|
+
...paginationSchema,
|
|
10
|
+
}, async ({ limit, offset, mostRecent }) => {
|
|
12
11
|
try {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
// Default behavior
|
|
13
|
+
if (!mostRecent || offset > 0) {
|
|
14
|
+
const defaultQueryParams = new URLSearchParams({
|
|
15
|
+
limit: limit.toString(),
|
|
16
|
+
offset: offset.toString(),
|
|
17
|
+
});
|
|
18
|
+
const defaultRes = await fetch(`${baseApiUrl}/api/v1/experiments?${defaultQueryParams.toString()}`, {
|
|
19
|
+
headers: {
|
|
20
|
+
Authorization: `Bearer ${apiKey}`,
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
await handleResNotOk(defaultRes);
|
|
25
|
+
const data = await defaultRes.json();
|
|
26
|
+
return {
|
|
27
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// Most recent behavior
|
|
31
|
+
const countRes = await fetch(`${baseApiUrl}/api/v1/experiments?limit=1`, {
|
|
32
|
+
headers: {
|
|
33
|
+
Authorization: `Bearer ${apiKey}`,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
await handleResNotOk(countRes);
|
|
37
|
+
const countData = await countRes.json();
|
|
38
|
+
const total = countData.total;
|
|
39
|
+
const calculatedOffset = Math.max(0, total - limit);
|
|
40
|
+
const mostRecentQueryParams = new URLSearchParams({
|
|
41
|
+
limit: limit.toString(),
|
|
42
|
+
offset: calculatedOffset.toString(),
|
|
16
43
|
});
|
|
17
|
-
const
|
|
44
|
+
const mostRecentRes = await fetch(`${baseApiUrl}/api/v1/experiments?${mostRecentQueryParams.toString()}`, {
|
|
18
45
|
headers: {
|
|
19
46
|
Authorization: `Bearer ${apiKey}`,
|
|
20
|
-
"Content-Type": "application/json",
|
|
21
47
|
},
|
|
22
48
|
});
|
|
23
|
-
await handleResNotOk(
|
|
24
|
-
const
|
|
49
|
+
await handleResNotOk(mostRecentRes);
|
|
50
|
+
const mostRecentData = await mostRecentRes.json();
|
|
51
|
+
if (mostRecentData.experiments &&
|
|
52
|
+
Array.isArray(mostRecentData.experiments)) {
|
|
53
|
+
mostRecentData.experiments = mostRecentData.experiments.reverse();
|
|
54
|
+
}
|
|
25
55
|
return {
|
|
26
|
-
content: [
|
|
56
|
+
content: [
|
|
57
|
+
{ type: "text", text: JSON.stringify(mostRecentData, null, 2) },
|
|
58
|
+
],
|
|
27
59
|
};
|
|
28
60
|
}
|
|
29
61
|
catch (error) {
|
package/build/tools/features.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { getDocsMetadata, handleResNotOk, generateLinkToGrowthBook, SUPPORTED_FILE_EXTENSIONS, } from "../utils.js";
|
|
2
|
+
import { getDocsMetadata, handleResNotOk, generateLinkToGrowthBook, SUPPORTED_FILE_EXTENSIONS, paginationSchema, } from "../utils.js";
|
|
3
3
|
import { exec } from "child_process";
|
|
4
4
|
import { getDefaults } from "./defaults.js";
|
|
5
5
|
export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, user, }) {
|
|
@@ -98,8 +98,7 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
|
|
|
98
98
|
* Tool: get_feature_flags
|
|
99
99
|
*/
|
|
100
100
|
server.tool("get_feature_flags", "Fetches all feature flags from the GrowthBook API, with optional limit, offset, and project filtering.", {
|
|
101
|
-
|
|
102
|
-
offset: z.number().optional().default(0),
|
|
101
|
+
...paginationSchema,
|
|
103
102
|
}, async ({ limit, offset }) => {
|
|
104
103
|
try {
|
|
105
104
|
const queryParams = new URLSearchParams({
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { generateLinkToGrowthBook, handleResNotOk, paginationSchema, } from "../utils.js";
|
|
3
|
+
export function registerMetricsTools({ server, baseApiUrl, apiKey, appOrigin, }) {
|
|
4
|
+
/**
|
|
5
|
+
* Tool: get_metrics
|
|
6
|
+
*/
|
|
7
|
+
server.tool("get_metrics", "Fetches metrics from the GrowthBook API", {
|
|
8
|
+
...paginationSchema,
|
|
9
|
+
}, async ({ limit, offset }) => {
|
|
10
|
+
try {
|
|
11
|
+
const queryParams = new URLSearchParams({
|
|
12
|
+
limit: limit?.toString(),
|
|
13
|
+
offset: offset?.toString(),
|
|
14
|
+
});
|
|
15
|
+
const metricsRes = await fetch(`${baseApiUrl}/api/v1/metrics?${queryParams.toString()}`, {
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: `Bearer ${apiKey}`,
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
await handleResNotOk(metricsRes);
|
|
22
|
+
const metricsData = await metricsRes.json();
|
|
23
|
+
const factMetricRes = await fetch(`${baseApiUrl}/api/v1/fact-metrics?${queryParams.toString()}`, {
|
|
24
|
+
headers: {
|
|
25
|
+
Authorization: `Bearer ${apiKey}`,
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
await handleResNotOk(factMetricRes);
|
|
30
|
+
const factMetricData = await factMetricRes.json();
|
|
31
|
+
const metricData = {
|
|
32
|
+
metrics: metricsData,
|
|
33
|
+
factMetrics: factMetricData,
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{ type: "text", text: JSON.stringify(metricData, null, 2) },
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new Error(`Error fetching metrics: ${error}`);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* Tool: get_metric
|
|
47
|
+
*/
|
|
48
|
+
server.tool("get_metric", "Fetches a metric from the GrowthBook API", {
|
|
49
|
+
metricId: z.string().describe("The ID of the metric to get"),
|
|
50
|
+
}, async ({ metricId }) => {
|
|
51
|
+
try {
|
|
52
|
+
let res;
|
|
53
|
+
if (metricId.startsWith("fact__")) {
|
|
54
|
+
res = await fetch(`${baseApiUrl}/api/v1/fact-metrics/${metricId}`, {
|
|
55
|
+
headers: {
|
|
56
|
+
Authorization: `Bearer ${apiKey}`,
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
res = await fetch(`${baseApiUrl}/api/v1/metrics/${metricId}`, {
|
|
63
|
+
headers: {
|
|
64
|
+
Authorization: `Bearer ${apiKey}`,
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
await handleResNotOk(res);
|
|
70
|
+
const data = await res.json();
|
|
71
|
+
const linkToGrowthBook = generateLinkToGrowthBook(appOrigin, data.factMetric ? "fact-metrics" : "metric", metricId);
|
|
72
|
+
return {
|
|
73
|
+
content: [
|
|
74
|
+
{
|
|
75
|
+
type: "text",
|
|
76
|
+
text: JSON.stringify(data, null, 2) +
|
|
77
|
+
`\n**Critical** Show the user the link to the metric in GrowthBook: [View the metric in GrowthBook](${linkToGrowthBook})
|
|
78
|
+
`,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw new Error(`Error fetching metric: ${error}`);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
package/build/tools/projects.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { handleResNotOk } from "../utils.js";
|
|
1
|
+
import { handleResNotOk, paginationSchema, } from "../utils.js";
|
|
3
2
|
/**
|
|
4
3
|
* Tool: get_projects
|
|
5
4
|
*/
|
|
6
5
|
export function registerProjectTools({ server, baseApiUrl, apiKey, }) {
|
|
7
6
|
server.tool("get_projects", "Fetches all projects from the GrowthBook API", {
|
|
8
|
-
|
|
9
|
-
offset: z.number().optional().default(0),
|
|
7
|
+
...paginationSchema,
|
|
10
8
|
}, async ({ limit, offset }) => {
|
|
11
9
|
const queryParams = new URLSearchParams({
|
|
12
10
|
limit: limit.toString(),
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { handleResNotOk } from "../utils.js";
|
|
2
|
+
import { handleResNotOk, paginationSchema, } from "../utils.js";
|
|
3
3
|
export function registerSdkConnectionTools({ server, baseApiUrl, apiKey, }) {
|
|
4
4
|
/**
|
|
5
5
|
* Tool: get_sdk_connections
|
|
6
6
|
*/
|
|
7
7
|
server.tool("get_sdk_connections", "Get all SDK connections. SDK connections are how GrowthBook connects to an app. Users need the client key to fetch features and experiments from the API.", {
|
|
8
|
-
|
|
9
|
-
offset: z.number().optional().default(0),
|
|
8
|
+
...paginationSchema,
|
|
10
9
|
}, async ({ limit, offset }) => {
|
|
11
10
|
try {
|
|
12
11
|
const queryParams = new URLSearchParams({
|
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 = [
|
|
@@ -172,3 +173,21 @@ export async function searchGrowthBookDocs(query) {
|
|
|
172
173
|
export function generateLinkToGrowthBook(appOrigin, resource, id) {
|
|
173
174
|
return `${appOrigin}/${resource}/${id}`;
|
|
174
175
|
}
|
|
176
|
+
// Reusable pagination schema for GrowthBook API tools
|
|
177
|
+
export const paginationSchema = {
|
|
178
|
+
limit: z
|
|
179
|
+
.number()
|
|
180
|
+
.min(1)
|
|
181
|
+
.max(100)
|
|
182
|
+
.default(100)
|
|
183
|
+
.describe("The number of items to fetch (1-100)"),
|
|
184
|
+
offset: z
|
|
185
|
+
.number()
|
|
186
|
+
.min(0)
|
|
187
|
+
.default(0)
|
|
188
|
+
.describe("The number of items to skip. For example, set to 100 to fetch the second page with default limit. Note: The API returns items in chronological order (oldest first) by default."),
|
|
189
|
+
mostRecent: z
|
|
190
|
+
.boolean()
|
|
191
|
+
.default(false)
|
|
192
|
+
.describe("When true, fetches the most recent items and returns them newest-first. When false (default), returns oldest items first."),
|
|
193
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growthbook/mcp",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MCP Server for interacting with GrowthBook",
|
|
5
5
|
"access": "public",
|
|
6
6
|
"homepage": "https://github.com/growthbook/growthbook-mcp",
|
|
7
7
|
"scripts": {
|
|
@@ -17,7 +17,13 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"build"
|
|
19
19
|
],
|
|
20
|
-
"keywords": [
|
|
20
|
+
"keywords": [
|
|
21
|
+
"growthbook",
|
|
22
|
+
"mcp",
|
|
23
|
+
"modelcontextprotocol",
|
|
24
|
+
"featureflags",
|
|
25
|
+
"experiments"
|
|
26
|
+
],
|
|
21
27
|
"author": "GrowthBook",
|
|
22
28
|
"license": "MIT",
|
|
23
29
|
"packageManager": "pnpm@10.6.1",
|