@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 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
- ### Cursor
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 all experiments from the GrowthBook API", {
9
- limit: z.number().optional().default(100),
10
- offset: z.number().optional().default(0),
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
- const queryParams = new URLSearchParams({
14
- limit: limit?.toString(),
15
- offset: offset?.toString(),
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 res = await fetch(`${baseApiUrl}/api/v1/experiments?${queryParams.toString()}`, {
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(res);
24
- const data = await res.json();
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: [{ type: "text", text: JSON.stringify(data, null, 2) }],
56
+ content: [
57
+ { type: "text", text: JSON.stringify(mostRecentData, null, 2) },
58
+ ],
27
59
  };
28
60
  }
29
61
  catch (error) {
@@ -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
- limit: z.number().optional().default(100),
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
+ }
@@ -1,12 +1,10 @@
1
- import { z } from "zod";
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
- limit: z.number().optional().default(100),
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
- limit: z.number().optional().default(100),
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.2",
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",