@growthbook/mcp 1.1.0 → 1.2.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/build/tools/environments.js +1 -3
- package/build/tools/experiments.js +17 -78
- package/build/tools/features.js +101 -41
- package/build/tools/metrics.js +9 -2
- package/build/tools/projects.js +1 -3
- package/build/tools/sdk-connections.js +15 -3
- package/build/utils.js +19 -0
- package/package.json +2 -1
|
@@ -18,9 +18,7 @@ export function registerEnvironmentTools({ server, baseApiUrl, apiKey, }) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
catch (error) {
|
|
21
|
-
|
|
22
|
-
content: [{ type: "text", text: `Error: ${error}` }],
|
|
23
|
-
};
|
|
21
|
+
throw new Error(`Error fetching environments: ${error}`);
|
|
24
22
|
}
|
|
25
23
|
});
|
|
26
24
|
}
|
|
@@ -6,8 +6,12 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
|
|
|
6
6
|
* Tool: get_experiments
|
|
7
7
|
*/
|
|
8
8
|
server.tool("get_experiments", "Fetches experiments from the GrowthBook API", {
|
|
9
|
+
project: z
|
|
10
|
+
.string()
|
|
11
|
+
.describe("The ID of the project to filter experiments by")
|
|
12
|
+
.optional(),
|
|
9
13
|
...paginationSchema,
|
|
10
|
-
}, async ({ limit, offset, mostRecent }) => {
|
|
14
|
+
}, async ({ limit, offset, mostRecent, project }) => {
|
|
11
15
|
try {
|
|
12
16
|
// Default behavior
|
|
13
17
|
if (!mostRecent || offset > 0) {
|
|
@@ -15,6 +19,9 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
|
|
|
15
19
|
limit: limit.toString(),
|
|
16
20
|
offset: offset.toString(),
|
|
17
21
|
});
|
|
22
|
+
if (project) {
|
|
23
|
+
defaultQueryParams.append("projectId", project);
|
|
24
|
+
}
|
|
18
25
|
const defaultRes = await fetch(`${baseApiUrl}/api/v1/experiments?${defaultQueryParams.toString()}`, {
|
|
19
26
|
headers: {
|
|
20
27
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -41,6 +48,9 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
|
|
|
41
48
|
limit: limit.toString(),
|
|
42
49
|
offset: calculatedOffset.toString(),
|
|
43
50
|
});
|
|
51
|
+
if (project) {
|
|
52
|
+
mostRecentQueryParams.append("projectId", project);
|
|
53
|
+
}
|
|
44
54
|
const mostRecentRes = await fetch(`${baseApiUrl}/api/v1/experiments?${mostRecentQueryParams.toString()}`, {
|
|
45
55
|
headers: {
|
|
46
56
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -62,81 +72,6 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
|
|
|
62
72
|
throw new Error(`Error fetching experiments: ${error}`);
|
|
63
73
|
}
|
|
64
74
|
});
|
|
65
|
-
/**
|
|
66
|
-
* Tool: create_force_rule
|
|
67
|
-
*/
|
|
68
|
-
server.tool("create_force_rule", "Create a new force rule on an existing feature. If the existing feature isn't apparent, create a new feature using create_feature_flag first. A force rule sets a feature to a specific value based on a condition. For A/B tests and experiments, use create_experiment instead.", {
|
|
69
|
-
featureId: z
|
|
70
|
-
.string()
|
|
71
|
-
.describe("The ID of the feature to create the rule on"),
|
|
72
|
-
description: z.string().optional(),
|
|
73
|
-
condition: z
|
|
74
|
-
.string()
|
|
75
|
-
.describe("Applied to everyone by default. Write conditions in MongoDB-style query syntax.")
|
|
76
|
-
.optional(),
|
|
77
|
-
value: z
|
|
78
|
-
.string()
|
|
79
|
-
.describe("The type of the value should match the feature type"),
|
|
80
|
-
fileExtension: z
|
|
81
|
-
.enum(SUPPORTED_FILE_EXTENSIONS)
|
|
82
|
-
.describe("The extension of the current file. If it's unclear, ask the user."),
|
|
83
|
-
}, async ({ featureId, description, condition, value, fileExtension }) => {
|
|
84
|
-
try {
|
|
85
|
-
// Fetch feature defaults first and surface to user
|
|
86
|
-
const defaults = await getDefaults(apiKey, baseApiUrl);
|
|
87
|
-
const defaultEnvironments = defaults.environments;
|
|
88
|
-
const payload = {
|
|
89
|
-
// Loop through the environments and create a rule for each one keyed by environment name
|
|
90
|
-
environments: defaultEnvironments.reduce((acc, env) => {
|
|
91
|
-
acc[env] = {
|
|
92
|
-
enabled: false,
|
|
93
|
-
rules: [
|
|
94
|
-
{
|
|
95
|
-
type: "force",
|
|
96
|
-
description,
|
|
97
|
-
condition,
|
|
98
|
-
value,
|
|
99
|
-
},
|
|
100
|
-
],
|
|
101
|
-
};
|
|
102
|
-
return acc;
|
|
103
|
-
}, {}),
|
|
104
|
-
};
|
|
105
|
-
const res = await fetch(`${baseApiUrl}/api/v1/features/${featureId}`, {
|
|
106
|
-
method: "POST",
|
|
107
|
-
headers: {
|
|
108
|
-
Authorization: `Bearer ${apiKey}`,
|
|
109
|
-
"Content-Type": "application/json",
|
|
110
|
-
},
|
|
111
|
-
body: JSON.stringify(payload),
|
|
112
|
-
});
|
|
113
|
-
await handleResNotOk(res);
|
|
114
|
-
const data = await res.json();
|
|
115
|
-
const linkToGrowthBook = generateLinkToGrowthBook(appOrigin, "features", featureId);
|
|
116
|
-
const { docs, language, stub } = getDocsMetadata(fileExtension);
|
|
117
|
-
const text = `This is the API response: ${JSON.stringify(data, null, 2)}
|
|
118
|
-
|
|
119
|
-
Additionally, here is a template of what to show to the user:
|
|
120
|
-
|
|
121
|
-
**✅ Your feature flag \`my-flag-name\` is ready!.**
|
|
122
|
-
[View it in GrowthBook](${linkToGrowthBook})
|
|
123
|
-
|
|
124
|
-
**How to use it in your code:**
|
|
125
|
-
(To the LLM agent. Use the text below to properly add the feature flag to the codebase. Also, surface the relevant docs/implemenation to the user)
|
|
126
|
-
|
|
127
|
-
${stub}
|
|
128
|
-
|
|
129
|
-
**Want to learn more?**
|
|
130
|
-
See the [GrowthBook ${language} docs](${docs})
|
|
131
|
-
`;
|
|
132
|
-
return {
|
|
133
|
-
content: [{ type: "text", text }],
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
throw new Error(`Error creating force rule: ${error}`);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
75
|
/**
|
|
141
76
|
* Tool: get_experiment
|
|
142
77
|
*/
|
|
@@ -201,7 +136,6 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
|
|
|
201
136
|
.string()
|
|
202
137
|
.optional()
|
|
203
138
|
.describe("Experiment hypothesis. Base hypothesis off the examples from get_defaults. If none are available, use a falsifiable statement about what will happen if the experiment succeeds or fails."),
|
|
204
|
-
value: z.string().describe("The default value of the experiment."),
|
|
205
139
|
variations: z
|
|
206
140
|
.array(z.object({
|
|
207
141
|
name: z
|
|
@@ -217,13 +151,17 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
|
|
|
217
151
|
.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."),
|
|
218
152
|
}))
|
|
219
153
|
.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."),
|
|
154
|
+
project: z
|
|
155
|
+
.string()
|
|
156
|
+
.describe("The ID of the project to create the experiment in")
|
|
157
|
+
.optional(),
|
|
220
158
|
fileExtension: z
|
|
221
159
|
.enum(SUPPORTED_FILE_EXTENSIONS)
|
|
222
160
|
.describe("The extension of the current file. If it's unclear, ask the user."),
|
|
223
161
|
confirmedDefaultsReviewed: z
|
|
224
162
|
.boolean()
|
|
225
163
|
.describe("Set to true to confirm you have called get_defaults and reviewed the output to guide these parameters."),
|
|
226
|
-
}, async ({ description, hypothesis, name, variations, fileExtension, confirmedDefaultsReviewed, }) => {
|
|
164
|
+
}, async ({ description, hypothesis, name, variations, fileExtension, confirmedDefaultsReviewed, project, }) => {
|
|
227
165
|
if (!confirmedDefaultsReviewed) {
|
|
228
166
|
return {
|
|
229
167
|
content: [
|
|
@@ -249,6 +187,7 @@ export function registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin,
|
|
|
249
187
|
key: idx.toString(),
|
|
250
188
|
name: variation.name,
|
|
251
189
|
})),
|
|
190
|
+
...(project && { project }),
|
|
252
191
|
};
|
|
253
192
|
try {
|
|
254
193
|
const experimentRes = await fetch(`${baseApiUrl}/api/v1/experiments`, {
|
package/build/tools/features.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { getDocsMetadata, handleResNotOk, generateLinkToGrowthBook,
|
|
2
|
+
import { getDocsMetadata, handleResNotOk, generateLinkToGrowthBook, paginationSchema, featureFlagSchema, } 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, }) {
|
|
@@ -7,25 +7,13 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
|
|
|
7
7
|
* Tool: create_feature_flag
|
|
8
8
|
*/
|
|
9
9
|
server.tool("create_feature_flag", "Creates a new feature flag in GrowthBook and modifies the codebase when relevant.", {
|
|
10
|
-
id:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.default("")
|
|
18
|
-
.describe("A brief description of the feature flag"),
|
|
19
|
-
valueType: z
|
|
20
|
-
.enum(["string", "number", "boolean", "json"])
|
|
21
|
-
.describe("The value type the feature flag will return"),
|
|
22
|
-
defaultValue: z
|
|
23
|
-
.string()
|
|
24
|
-
.describe("The default value of the feature flag"),
|
|
25
|
-
fileExtension: z
|
|
26
|
-
.enum(SUPPORTED_FILE_EXTENSIONS)
|
|
27
|
-
.describe("The extension of the current file. If it's unclear, ask the user."),
|
|
28
|
-
}, async ({ id, description, valueType, defaultValue, fileExtension }) => {
|
|
10
|
+
id: featureFlagSchema.id,
|
|
11
|
+
valueType: featureFlagSchema.valueType,
|
|
12
|
+
defaultValue: featureFlagSchema.defaultValue,
|
|
13
|
+
description: featureFlagSchema.description.optional().default(""),
|
|
14
|
+
project: featureFlagSchema.project.optional(),
|
|
15
|
+
fileExtension: featureFlagSchema.fileExtension,
|
|
16
|
+
}, async ({ id, valueType, defaultValue, description, project, fileExtension, }) => {
|
|
29
17
|
// get environments
|
|
30
18
|
let environments = [];
|
|
31
19
|
const defaults = await getDefaults(apiKey, baseApiUrl);
|
|
@@ -57,6 +45,7 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
|
|
|
57
45
|
};
|
|
58
46
|
return acc;
|
|
59
47
|
}, {}),
|
|
48
|
+
...(project && { project }),
|
|
60
49
|
};
|
|
61
50
|
try {
|
|
62
51
|
const res = await fetch(`${baseApiUrl}/api/v1/features`, {
|
|
@@ -72,6 +61,77 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
|
|
|
72
61
|
const { docs, language, stub } = getDocsMetadata(fileExtension);
|
|
73
62
|
const linkToGrowthBook = generateLinkToGrowthBook(appOrigin, "features", id);
|
|
74
63
|
const text = `This is the API response: ${JSON.stringify(data, null, 2)}
|
|
64
|
+
|
|
65
|
+
Additionally, here is a template of what to show to the user:
|
|
66
|
+
|
|
67
|
+
**✅ Your feature flag \`my-flag-name\` is ready!**
|
|
68
|
+
[View it in GrowthBook](${linkToGrowthBook})
|
|
69
|
+
|
|
70
|
+
**How to use it in your code:**
|
|
71
|
+
(To the LLM agent. Use the text below to properly add the feature flag to the codebase. Also, surface the relevant docs/implemenation to the user)
|
|
72
|
+
|
|
73
|
+
${stub}
|
|
74
|
+
|
|
75
|
+
**Want to learn more?**
|
|
76
|
+
See the [GrowthBook ${language} docs](${docs})
|
|
77
|
+
`;
|
|
78
|
+
return {
|
|
79
|
+
content: [{ type: "text", text }],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw new Error(`Error creating feature flag: ${error}`);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* Tool: create_force_rule
|
|
88
|
+
*/
|
|
89
|
+
server.tool("create_force_rule", "Create a new force rule on an existing feature. If the existing feature isn't apparent, create a new feature using create_feature_flag first. A force rule sets a feature to a specific value based on a condition. For A/B tests and experiments, use create_experiment instead.", {
|
|
90
|
+
featureId: featureFlagSchema.id,
|
|
91
|
+
description: featureFlagSchema.description.optional().default(""),
|
|
92
|
+
fileExtension: featureFlagSchema.fileExtension,
|
|
93
|
+
condition: z
|
|
94
|
+
.string()
|
|
95
|
+
.describe("Applied to everyone by default. Write conditions in MongoDB-style query syntax.")
|
|
96
|
+
.optional(),
|
|
97
|
+
value: z
|
|
98
|
+
.string()
|
|
99
|
+
.describe("The type of the value should match the feature type"),
|
|
100
|
+
}, async ({ featureId, description, condition, value, fileExtension }) => {
|
|
101
|
+
try {
|
|
102
|
+
// Fetch feature defaults first and surface to user
|
|
103
|
+
const defaults = await getDefaults(apiKey, baseApiUrl);
|
|
104
|
+
const defaultEnvironments = defaults.environments;
|
|
105
|
+
const payload = {
|
|
106
|
+
// Loop through the environments and create a rule for each one keyed by environment name
|
|
107
|
+
environments: defaultEnvironments.reduce((acc, env) => {
|
|
108
|
+
acc[env] = {
|
|
109
|
+
enabled: false,
|
|
110
|
+
rules: [
|
|
111
|
+
{
|
|
112
|
+
type: "force",
|
|
113
|
+
description,
|
|
114
|
+
condition,
|
|
115
|
+
value,
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
return acc;
|
|
120
|
+
}, {}),
|
|
121
|
+
};
|
|
122
|
+
const res = await fetch(`${baseApiUrl}/api/v1/features/${featureId}`, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: {
|
|
125
|
+
Authorization: `Bearer ${apiKey}`,
|
|
126
|
+
"Content-Type": "application/json",
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify(payload),
|
|
129
|
+
});
|
|
130
|
+
await handleResNotOk(res);
|
|
131
|
+
const data = await res.json();
|
|
132
|
+
const linkToGrowthBook = generateLinkToGrowthBook(appOrigin, "features", featureId);
|
|
133
|
+
const { docs, language, stub } = getDocsMetadata(fileExtension);
|
|
134
|
+
const text = `This is the API response: ${JSON.stringify(data, null, 2)}
|
|
75
135
|
|
|
76
136
|
Additionally, here is a template of what to show to the user:
|
|
77
137
|
|
|
@@ -91,20 +151,24 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
|
|
|
91
151
|
};
|
|
92
152
|
}
|
|
93
153
|
catch (error) {
|
|
94
|
-
throw new Error(`Error creating
|
|
154
|
+
throw new Error(`Error creating force rule: ${error}`);
|
|
95
155
|
}
|
|
96
156
|
});
|
|
97
157
|
/**
|
|
98
158
|
* Tool: get_feature_flags
|
|
99
159
|
*/
|
|
100
160
|
server.tool("get_feature_flags", "Fetches all feature flags from the GrowthBook API, with optional limit, offset, and project filtering.", {
|
|
161
|
+
project: featureFlagSchema.project.optional(),
|
|
101
162
|
...paginationSchema,
|
|
102
|
-
}, async ({ limit, offset }) => {
|
|
163
|
+
}, async ({ limit, offset, project }) => {
|
|
103
164
|
try {
|
|
104
165
|
const queryParams = new URLSearchParams({
|
|
105
166
|
limit: limit?.toString(),
|
|
106
167
|
offset: offset?.toString(),
|
|
107
168
|
});
|
|
169
|
+
if (project) {
|
|
170
|
+
queryParams.append("projectId", project);
|
|
171
|
+
}
|
|
108
172
|
const res = await fetch(`${baseApiUrl}/api/v1/features?${queryParams.toString()}`, {
|
|
109
173
|
headers: {
|
|
110
174
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -125,14 +189,10 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
|
|
|
125
189
|
* Tool: get_single_feature_flag
|
|
126
190
|
*/
|
|
127
191
|
server.tool("get_single_feature_flag", "Fetches a specific feature flag from the GrowthBook API", {
|
|
128
|
-
id:
|
|
129
|
-
|
|
130
|
-
}, async ({ id, project }) => {
|
|
192
|
+
id: featureFlagSchema.id,
|
|
193
|
+
}, async ({ id }) => {
|
|
131
194
|
try {
|
|
132
|
-
const
|
|
133
|
-
if (project)
|
|
134
|
-
queryParams.append("project", project);
|
|
135
|
-
const res = await fetch(`${baseApiUrl}/api/v1/features/${id}?${queryParams.toString()}`, {
|
|
195
|
+
const res = await fetch(`${baseApiUrl}/api/v1/features/${id}`, {
|
|
136
196
|
headers: {
|
|
137
197
|
Authorization: `Bearer ${apiKey}`,
|
|
138
198
|
"Content-Type": "application/json",
|
|
@@ -143,11 +203,11 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
|
|
|
143
203
|
const linkToGrowthBook = generateLinkToGrowthBook(appOrigin, "features", id);
|
|
144
204
|
const text = `
|
|
145
205
|
${JSON.stringify(data.feature, null, 2)}
|
|
146
|
-
|
|
206
|
+
|
|
147
207
|
Share information about the feature flag with the user. In particular, give details about the enabled environments,
|
|
148
|
-
rules for each environment, and the default value. If the feature flag is archived or doesnt exist, inform the user and
|
|
149
|
-
ask if they want to remove references to the feature flag from the codebase.
|
|
150
|
-
|
|
208
|
+
rules for each environment, and the default value. If the feature flag is archived or doesnt exist, inform the user and
|
|
209
|
+
ask if they want to remove references to the feature flag from the codebase.
|
|
210
|
+
|
|
151
211
|
[View it in GrowthBook](${linkToGrowthBook})
|
|
152
212
|
`;
|
|
153
213
|
return {
|
|
@@ -193,14 +253,14 @@ export function registerFeatureTools({ server, baseApiUrl, apiKey, appOrigin, us
|
|
|
193
253
|
});
|
|
194
254
|
});
|
|
195
255
|
const text = `
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
256
|
+
${JSON.stringify(filteredSafeRollouts, null, 2)}
|
|
257
|
+
|
|
258
|
+
Share information about the rolled-back or released safe rollout rules with the user. Safe Rollout rules are stored under
|
|
259
|
+
environmentSettings, keyed by environment and are within the rules array with a type of "safe-rollout". Ask the user if they
|
|
260
|
+
would like to remove references to the feature associated with the rolled-back or released safe rollout rules and if they do,
|
|
261
|
+
remove the references and associated GrowthBook code and replace the values with controlValue if the safe rollout rule is rolled-back or with the
|
|
262
|
+
variationValue if the safe rollout is released. In addition to the current file, you may need to update other files in the codebase.
|
|
263
|
+
`;
|
|
204
264
|
return {
|
|
205
265
|
content: [{ type: "text", text }],
|
|
206
266
|
};
|
package/build/tools/metrics.js
CHANGED
|
@@ -4,14 +4,21 @@ export function registerMetricsTools({ server, baseApiUrl, apiKey, appOrigin, })
|
|
|
4
4
|
/**
|
|
5
5
|
* Tool: get_metrics
|
|
6
6
|
*/
|
|
7
|
-
server.tool("get_metrics", "Fetches metrics from the GrowthBook API", {
|
|
7
|
+
server.tool("get_metrics", "Fetches metrics from the GrowthBook API, with optional limit, offset, and project filtering.", {
|
|
8
|
+
project: z
|
|
9
|
+
.string()
|
|
10
|
+
.describe("The ID of the project to filter metrics by")
|
|
11
|
+
.optional(),
|
|
8
12
|
...paginationSchema,
|
|
9
|
-
}, async ({ limit, offset }) => {
|
|
13
|
+
}, async ({ limit, offset, project }) => {
|
|
10
14
|
try {
|
|
11
15
|
const queryParams = new URLSearchParams({
|
|
12
16
|
limit: limit?.toString(),
|
|
13
17
|
offset: offset?.toString(),
|
|
14
18
|
});
|
|
19
|
+
if (project) {
|
|
20
|
+
queryParams.append("projectId", project);
|
|
21
|
+
}
|
|
15
22
|
const metricsRes = await fetch(`${baseApiUrl}/api/v1/metrics?${queryParams.toString()}`, {
|
|
16
23
|
headers: {
|
|
17
24
|
Authorization: `Bearer ${apiKey}`,
|
package/build/tools/projects.js
CHANGED
|
@@ -5,13 +5,20 @@ export function registerSdkConnectionTools({ server, baseApiUrl, apiKey, }) {
|
|
|
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
|
+
project: z
|
|
9
|
+
.string()
|
|
10
|
+
.describe("The ID of the project to filter SDK connections by")
|
|
11
|
+
.optional(),
|
|
8
12
|
...paginationSchema,
|
|
9
|
-
}, async ({ limit, offset }) => {
|
|
13
|
+
}, async ({ limit, offset, project }) => {
|
|
10
14
|
try {
|
|
11
15
|
const queryParams = new URLSearchParams({
|
|
12
16
|
limit: limit?.toString(),
|
|
13
17
|
offset: offset?.toString(),
|
|
14
18
|
});
|
|
19
|
+
if (project) {
|
|
20
|
+
queryParams.append("projectId", project);
|
|
21
|
+
}
|
|
15
22
|
const res = await fetch(`${baseApiUrl}/api/v1/sdk-connections?${queryParams.toString()}`, {
|
|
16
23
|
headers: {
|
|
17
24
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -60,12 +67,16 @@ export function registerSdkConnectionTools({ server, baseApiUrl, apiKey, }) {
|
|
|
60
67
|
"edge-other",
|
|
61
68
|
"other",
|
|
62
69
|
])
|
|
63
|
-
.describe("The language
|
|
70
|
+
.describe("The language or platform for the SDK connection."),
|
|
64
71
|
environment: z
|
|
65
72
|
.string()
|
|
66
73
|
.optional()
|
|
67
74
|
.describe("The environment associated with the SDK connection."),
|
|
68
|
-
|
|
75
|
+
projects: z
|
|
76
|
+
.array(z.string())
|
|
77
|
+
.describe("The projects to create the SDK connection in")
|
|
78
|
+
.optional(),
|
|
79
|
+
}, async ({ name, language, environment, projects }) => {
|
|
69
80
|
if (!environment) {
|
|
70
81
|
try {
|
|
71
82
|
const res = await fetch(`${baseApiUrl}/api/v1/environments`, {
|
|
@@ -94,6 +105,7 @@ export function registerSdkConnectionTools({ server, baseApiUrl, apiKey, }) {
|
|
|
94
105
|
name,
|
|
95
106
|
language,
|
|
96
107
|
environment,
|
|
108
|
+
...(projects && { projects }),
|
|
97
109
|
};
|
|
98
110
|
try {
|
|
99
111
|
const res = await fetch(`${baseApiUrl}/api/v1/sdk-connections`, {
|
package/build/utils.js
CHANGED
|
@@ -191,3 +191,22 @@ export const paginationSchema = {
|
|
|
191
191
|
.default(false)
|
|
192
192
|
.describe("When true, fetches the most recent items and returns them newest-first. When false (default), returns oldest items first."),
|
|
193
193
|
};
|
|
194
|
+
export const featureFlagSchema = {
|
|
195
|
+
id: z
|
|
196
|
+
.string()
|
|
197
|
+
.regex(/^[a-zA-Z0-9_.:|_-]+$/, "Feature key can only include letters, numbers, and the characters _, -, ., :, and |")
|
|
198
|
+
.describe("A unique key name for the feature"),
|
|
199
|
+
valueType: z
|
|
200
|
+
.enum(["string", "number", "boolean", "json"])
|
|
201
|
+
.describe("The value type the feature flag will return"),
|
|
202
|
+
defaultValue: z.string().describe("The default value of the feature flag"),
|
|
203
|
+
description: z.string().describe("A brief description of the feature flag"),
|
|
204
|
+
archived: z.boolean().describe("Whether the feature flag should be archived"),
|
|
205
|
+
project: z
|
|
206
|
+
.string()
|
|
207
|
+
.describe("The ID of the project to which the feature flag belongs"),
|
|
208
|
+
// Contextual info
|
|
209
|
+
fileExtension: z
|
|
210
|
+
.enum(SUPPORTED_FILE_EXTENSIONS)
|
|
211
|
+
.describe("The extension of the current file. If it's unclear, ask the user."),
|
|
212
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@growthbook/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "MCP Server for interacting with GrowthBook",
|
|
5
5
|
"access": "public",
|
|
6
6
|
"homepage": "https://github.com/growthbook/growthbook-mcp",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
9
|
"build": "tsc",
|
|
10
|
+
"dev": "tsc --watch",
|
|
10
11
|
"bump:patch": "pnpm version patch --no-git-tag-version",
|
|
11
12
|
"bump:minor": "pnpm version minor --no-git-tag-version",
|
|
12
13
|
"bump:major": "pnpm version major --no-git-tag-version"
|