get_experiments
Retrieve experiments from the GrowthBook API with customizable parameters like limit, offset, and project for targeted data access.
Instructions
Fetches all experiments from the GrowthBook API
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| offset | No | ||
| project | No |
Input Schema (JSON Schema)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"limit": {
"default": 100,
"type": "number"
},
"offset": {
"default": 0,
"type": "number"
},
"project": {
"type": "string"
}
},
"type": "object"
}
Implementation Reference
- src/tools/experiments.ts:63-195 (handler)The handler function fetches experiments from GrowthBook API. Supports pagination (limit/offset), project filter, 'mostRecent' sorting, and 'analyze' mode to include results. Returns JSON stringified response.async ({ limit, offset, mostRecent, project, mode }) => { try { // Default behavior if (!mostRecent || offset > 0) { const defaultQueryParams = new URLSearchParams({ limit: limit.toString(), offset: offset.toString(), }); if (project) { defaultQueryParams.append("projectId", project); } const defaultRes = await fetch( `${baseApiUrl}/api/v1/experiments?${defaultQueryParams.toString()}`, { headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, } ); await handleResNotOk(defaultRes); const data = await defaultRes.json(); const experiments = data.experiments as Experiment[]; if (mode === "analyze") { for (const [index, experiment] of experiments.entries()) { try { const resultsRes = await fetch( `${baseApiUrl}/api/v1/experiments/${experiment.id}/results`, { headers: { Authorization: `Bearer ${apiKey}`, }, } ); await handleResNotOk(resultsRes); const resultsData = await resultsRes.json(); experiments[index].result = resultsData.result; } catch (error) { console.error( `Error fetching results for experiment ${experiment.id} (${experiment.name})`, error ); } } } return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }], }; } // Most recent behavior const countRes = await fetch( `${baseApiUrl}/api/v1/experiments?limit=1`, { headers: { Authorization: `Bearer ${apiKey}`, }, } ); await handleResNotOk(countRes); const countData = await countRes.json(); const total = countData.total; const calculatedOffset = Math.max(0, total - limit); const mostRecentQueryParams = new URLSearchParams({ limit: limit.toString(), offset: calculatedOffset.toString(), }); if (project) { mostRecentQueryParams.append("projectId", project); } const mostRecentRes = await fetch( `${baseApiUrl}/api/v1/experiments?${mostRecentQueryParams.toString()}`, { headers: { Authorization: `Bearer ${apiKey}`, }, } ); await handleResNotOk(mostRecentRes); const mostRecentData = await mostRecentRes.json(); if ( mostRecentData.experiments && Array.isArray(mostRecentData.experiments) ) { mostRecentData.experiments = mostRecentData.experiments.reverse(); if (mode === "analyze") { for (const [ index, experiment, ] of mostRecentData.experiments.entries()) { try { const resultsRes = await fetch( `${baseApiUrl}/api/v1/experiments/${experiment.id}/results`, { headers: { Authorization: `Bearer ${apiKey}`, }, } ); await handleResNotOk(resultsRes); const resultsData = await resultsRes.json(); mostRecentData.experiments[index].result = resultsData.result; } catch (error) { console.error( `Error fetching results for experiment ${experiment.id} (${experiment.name})`, error ); } } } } return { content: [ { type: "text", text: JSON.stringify(mostRecentData, null, 2) }, ], }; } catch (error) { throw new Error(`Error fetching experiments: ${error}`); } }
- src/tools/experiments.ts:47-59 (schema)Zod input schema defining parameters: optional project ID, mode (default/analyze), and pagination options from paginationSchema.{ project: z .string() .describe("The ID of the project to filter experiments by") .optional(), mode: z .enum(["default", "analyze"]) .default("default") .describe( "The mode to use to fetch experiments. Default mode returns summary info about experiments. Analyze mode will also fetch experiment results, allowing for better analysis, interpretation, and reporting." ), ...paginationSchema, },
- src/tools/experiments.ts:44-196 (registration)Registers the get_experiments tool on the MCP server with name, description, input schema, readOnlyHint, and handler function.server.tool( "get_experiments", "Fetches experiments from the GrowthBook API", { project: z .string() .describe("The ID of the project to filter experiments by") .optional(), mode: z .enum(["default", "analyze"]) .default("default") .describe( "The mode to use to fetch experiments. Default mode returns summary info about experiments. Analyze mode will also fetch experiment results, allowing for better analysis, interpretation, and reporting." ), ...paginationSchema, }, { readOnlyHint: true, }, async ({ limit, offset, mostRecent, project, mode }) => { try { // Default behavior if (!mostRecent || offset > 0) { const defaultQueryParams = new URLSearchParams({ limit: limit.toString(), offset: offset.toString(), }); if (project) { defaultQueryParams.append("projectId", project); } const defaultRes = await fetch( `${baseApiUrl}/api/v1/experiments?${defaultQueryParams.toString()}`, { headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, } ); await handleResNotOk(defaultRes); const data = await defaultRes.json(); const experiments = data.experiments as Experiment[]; if (mode === "analyze") { for (const [index, experiment] of experiments.entries()) { try { const resultsRes = await fetch( `${baseApiUrl}/api/v1/experiments/${experiment.id}/results`, { headers: { Authorization: `Bearer ${apiKey}`, }, } ); await handleResNotOk(resultsRes); const resultsData = await resultsRes.json(); experiments[index].result = resultsData.result; } catch (error) { console.error( `Error fetching results for experiment ${experiment.id} (${experiment.name})`, error ); } } } return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }], }; } // Most recent behavior const countRes = await fetch( `${baseApiUrl}/api/v1/experiments?limit=1`, { headers: { Authorization: `Bearer ${apiKey}`, }, } ); await handleResNotOk(countRes); const countData = await countRes.json(); const total = countData.total; const calculatedOffset = Math.max(0, total - limit); const mostRecentQueryParams = new URLSearchParams({ limit: limit.toString(), offset: calculatedOffset.toString(), }); if (project) { mostRecentQueryParams.append("projectId", project); } const mostRecentRes = await fetch( `${baseApiUrl}/api/v1/experiments?${mostRecentQueryParams.toString()}`, { headers: { Authorization: `Bearer ${apiKey}`, }, } ); await handleResNotOk(mostRecentRes); const mostRecentData = await mostRecentRes.json(); if ( mostRecentData.experiments && Array.isArray(mostRecentData.experiments) ) { mostRecentData.experiments = mostRecentData.experiments.reverse(); if (mode === "analyze") { for (const [ index, experiment, ] of mostRecentData.experiments.entries()) { try { const resultsRes = await fetch( `${baseApiUrl}/api/v1/experiments/${experiment.id}/results`, { headers: { Authorization: `Bearer ${apiKey}`, }, } ); await handleResNotOk(resultsRes); const resultsData = await resultsRes.json(); mostRecentData.experiments[index].result = resultsData.result; } catch (error) { console.error( `Error fetching results for experiment ${experiment.id} (${experiment.name})`, error ); } } } } return { content: [ { type: "text", text: JSON.stringify(mostRecentData, null, 2) }, ], }; } catch (error) { throw new Error(`Error fetching experiments: ${error}`); } } );
- src/index.ts:89-95 (registration)Top-level call to registerExperimentTools in the main MCP server setup, which includes registering get_experiments.registerExperimentTools({ server, baseApiUrl, apiKey, appOrigin, user, });
- src/tools/experiments.ts:14-32 (helper)TypeScript type definition for Experiment used in the handler to type API response.type Experiment = { id: string; trackingKey: string; dateCreated: string; dateUpdated: string; name: string; type: "standard"; project: string; resultSummary: { status: string; winner: string; conclusions: string; releasedVariationId: string; excludeFromPayload: true; }; result?: { [key: string]: any; }; };