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
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| offset | No | ||
| project | No |
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; }; };