optimizely-mcp-server 1.0.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 ADDED
@@ -0,0 +1,115 @@
1
+ # Optimizely MCP Server
2
+
3
+ An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that connects AI assistants to the Optimizely REST API. Manage experiments, feature flags, audiences, and more without leaving your editor.
4
+
5
+ ## Quick Start
6
+
7
+ ### 1. Get an Optimizely API Token
8
+
9
+ - Log into [app.optimizely.com](https://app.optimizely.com)
10
+ - Go to **Profile → API Access**
11
+ - Click **Generate New Token**
12
+
13
+ ### 2. Add to Cursor
14
+
15
+ Add this to your `~/.cursor/mcp.json`:
16
+
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "Optimizely": {
21
+ "command": "npx",
22
+ "args": ["-y", "optimizely-mcp-server"],
23
+ "env": {
24
+ "OPTIMIZELY_API_TOKEN": "<your-token-here>"
25
+ }
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ Restart Cursor and the Optimizely tools will be available in any chat.
32
+
33
+ ## Available Tools
34
+
35
+ ### Projects
36
+ | Tool | Description |
37
+ |------|-------------|
38
+ | `list_projects` | List all projects in your account |
39
+ | `get_project` | Get details for a specific project |
40
+
41
+ ### Experiments
42
+ | Tool | Description |
43
+ |------|-------------|
44
+ | `list_experiments` | List experiments in a project |
45
+ | `get_experiment` | Get full experiment details |
46
+ | `create_experiment` | Create a new A/B test |
47
+ | `update_experiment` | Update or start/pause an experiment |
48
+ | `get_experiment_results` | Get conversion rates and statistical significance |
49
+
50
+ ### Feature Flags
51
+ | Tool | Description |
52
+ |------|-------------|
53
+ | `list_flags` | List all feature flags in a project |
54
+ | `get_flag` | Get flag details including variables and rules |
55
+ | `create_flag` | Create a new feature flag |
56
+ | `update_flag` | Update a flag's name, description, or archive it |
57
+ | `toggle_flag` | Enable or disable a flag in a specific environment |
58
+ | `list_flag_rules` | List rules (rollouts, A/B tests) for a flag |
59
+ | `list_flag_variations` | List all variations for a flag |
60
+ | `create_flag_variation` | Create a new variation with variable values |
61
+
62
+ ### Audiences & Targeting
63
+ | Tool | Description |
64
+ |------|-------------|
65
+ | `list_audiences` | List all audiences in a project |
66
+ | `get_audience` | Get audience targeting conditions |
67
+ | `list_attributes` | List custom attributes used for targeting |
68
+
69
+ ### Campaigns & Results
70
+ | Tool | Description |
71
+ |------|-------------|
72
+ | `list_campaigns` | List all campaigns in a project |
73
+ | `get_campaign_results` | Get campaign metrics and significance |
74
+
75
+ ### Other
76
+ | Tool | Description |
77
+ |------|-------------|
78
+ | `list_events` | List tracked events/goals |
79
+ | `list_environments` | List environments (production, staging, etc.) |
80
+ | `list_pages` | List pages (URL targeting) |
81
+ | `list_changes` | View recent change history |
82
+ | `search` | Search across all resources by keyword |
83
+
84
+ ## Environment Variables
85
+
86
+ | Variable | Required | Description |
87
+ |----------|----------|-------------|
88
+ | `OPTIMIZELY_API_TOKEN` | Yes | Your Optimizely personal access token |
89
+
90
+ ## Development
91
+
92
+ ```bash
93
+ git clone <repo-url>
94
+ cd optimizely-mcp
95
+ npm install
96
+ npm run build
97
+ ```
98
+
99
+ To test locally, update your `mcp.json` to point at the local build:
100
+
101
+ ```json
102
+ {
103
+ "Optimizely": {
104
+ "command": "node",
105
+ "args": ["/path/to/optimizely-mcp/dist/index.js"],
106
+ "env": {
107
+ "OPTIMIZELY_API_TOKEN": "<your-token>"
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ ## License
114
+
115
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,371 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { OptimizelyClient } from "./optimizely-client.js";
6
+ const token = process.env.OPTIMIZELY_API_TOKEN;
7
+ if (!token) {
8
+ console.error("OPTIMIZELY_API_TOKEN environment variable is required");
9
+ process.exit(1);
10
+ }
11
+ const client = new OptimizelyClient(token);
12
+ const server = new McpServer({
13
+ name: "Optimizely",
14
+ version: "1.0.0",
15
+ });
16
+ // ─── Projects ────────────────────────────────────────────────────────────────
17
+ server.tool("list_projects", "List all Optimizely projects in your account. Returns project IDs, names, status, and platform.", {
18
+ page: z.number().optional().describe("Page number (default 1)"),
19
+ per_page: z
20
+ .number()
21
+ .optional()
22
+ .describe("Results per page (default 25, max 100)"),
23
+ }, async ({ page, per_page }) => {
24
+ const data = await client.request({
25
+ path: "/v2/projects",
26
+ params: { page, per_page },
27
+ });
28
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
29
+ });
30
+ server.tool("get_project", "Get details for a specific Optimizely project by ID.", {
31
+ project_id: z.number().describe("The project ID"),
32
+ }, async ({ project_id }) => {
33
+ const data = await client.request({
34
+ path: `/v2/projects/${project_id}`,
35
+ });
36
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
37
+ });
38
+ // ─── Experiments ─────────────────────────────────────────────────────────────
39
+ server.tool("list_experiments", "List experiments in a project. Returns experiment IDs, names, status, type, and variation details.", {
40
+ project_id: z.number().describe("The project ID"),
41
+ page: z.number().optional().describe("Page number"),
42
+ per_page: z.number().optional().describe("Results per page (max 100)"),
43
+ }, async ({ project_id, page, per_page }) => {
44
+ const data = await client.request({
45
+ path: "/v2/experiments",
46
+ params: { project_id, page, per_page },
47
+ });
48
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
49
+ });
50
+ server.tool("get_experiment", "Get full details of a specific experiment including variations, metrics, traffic allocation, and status.", {
51
+ experiment_id: z.number().describe("The experiment ID"),
52
+ }, async ({ experiment_id }) => {
53
+ const data = await client.request({
54
+ path: `/v2/experiments/${experiment_id}`,
55
+ });
56
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
57
+ });
58
+ server.tool("create_experiment", "Create a new A/B test experiment in a project. Requires project_id, name, and at least one variation.", {
59
+ project_id: z.number().describe("The project ID to create the experiment in"),
60
+ name: z.string().describe("Experiment name"),
61
+ description: z.string().optional().describe("Experiment description"),
62
+ type: z
63
+ .enum(["a/b", "multivariate", "multiarmed_bandit"])
64
+ .optional()
65
+ .describe("Experiment type (default a/b)"),
66
+ variations: z
67
+ .array(z.object({
68
+ name: z.string().describe("Variation name"),
69
+ weight: z.number().optional().describe("Traffic weight (0-10000, representing basis points)"),
70
+ actions: z.array(z.any()).optional().describe("Variation actions/changes"),
71
+ }))
72
+ .describe("Array of variations for the experiment"),
73
+ metrics: z.array(z.any()).optional().describe("Array of metric objects"),
74
+ holdback: z.number().optional().describe("Holdback percentage (0-10000 basis points)"),
75
+ }, async ({ project_id, name, description, type, variations, metrics, holdback }) => {
76
+ const body = {
77
+ project_id,
78
+ name,
79
+ variations,
80
+ };
81
+ if (description)
82
+ body.description = description;
83
+ if (type)
84
+ body.type = type;
85
+ if (metrics)
86
+ body.metrics = metrics;
87
+ if (holdback !== undefined)
88
+ body.holdback = holdback;
89
+ const data = await client.request({
90
+ method: "POST",
91
+ path: "/v2/experiments",
92
+ body,
93
+ });
94
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
95
+ });
96
+ server.tool("update_experiment", "Update an existing experiment. Use this to change name, description, status (start/pause/archive), traffic allocation, variations, or metrics.", {
97
+ experiment_id: z.number().describe("The experiment ID to update"),
98
+ name: z.string().optional().describe("New experiment name"),
99
+ description: z.string().optional().describe("New description"),
100
+ status: z
101
+ .enum(["not_started", "running", "paused", "archived"])
102
+ .optional()
103
+ .describe("Change experiment status (e.g. 'running' to start, 'paused' to pause)"),
104
+ variations: z.array(z.any()).optional().describe("Updated variations"),
105
+ metrics: z.array(z.any()).optional().describe("Updated metrics"),
106
+ holdback: z.number().optional().describe("Updated holdback (0-10000)"),
107
+ }, async ({ experiment_id, ...updates }) => {
108
+ const body = {};
109
+ for (const [key, value] of Object.entries(updates)) {
110
+ if (value !== undefined)
111
+ body[key] = value;
112
+ }
113
+ const data = await client.request({
114
+ method: "PATCH",
115
+ path: `/v2/experiments/${experiment_id}`,
116
+ body,
117
+ });
118
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
119
+ });
120
+ server.tool("get_experiment_results", "Get results/statistics for an experiment, including conversion rates, statistical significance, and improvement.", {
121
+ experiment_id: z.number().describe("The experiment ID"),
122
+ start_time: z.string().optional().describe("Start time (ISO 8601 format)"),
123
+ end_time: z.string().optional().describe("End time (ISO 8601 format)"),
124
+ }, async ({ experiment_id, start_time, end_time }) => {
125
+ const data = await client.request({
126
+ path: `/v2/experiments/${experiment_id}/results`,
127
+ params: { start_time, end_time },
128
+ });
129
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
130
+ });
131
+ // ─── Feature Flags (Flags v1 API) ───────────────────────────────────────────
132
+ server.tool("list_flags", "List all feature flags in a project. Returns flag keys, names, descriptions, variables, and environments.", {
133
+ project_id: z.number().describe("The project ID"),
134
+ archived: z
135
+ .boolean()
136
+ .optional()
137
+ .describe("If true, return archived flags only"),
138
+ page: z.number().optional().describe("Page number"),
139
+ per_page: z.number().optional().describe("Results per page"),
140
+ }, async ({ project_id, archived, page, per_page }) => {
141
+ const params = { page, per_page };
142
+ if (archived !== undefined)
143
+ params.archived = archived ? "true" : "false";
144
+ const data = await client.request({
145
+ path: `/flags/v1/projects/${project_id}/flags`,
146
+ params,
147
+ });
148
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
149
+ });
150
+ server.tool("get_flag", "Get details of a specific feature flag by its key, including variables, variations, environments, and rules.", {
151
+ project_id: z.number().describe("The project ID"),
152
+ flag_key: z.string().describe("The flag key"),
153
+ }, async ({ project_id, flag_key }) => {
154
+ const data = await client.request({
155
+ path: `/flags/v1/projects/${project_id}/flags/${flag_key}`,
156
+ });
157
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
158
+ });
159
+ server.tool("create_flag", "Create a new feature flag in a project.", {
160
+ project_id: z.number().describe("The project ID"),
161
+ key: z.string().describe("Unique flag key (alphanumeric, underscores, hyphens)"),
162
+ name: z.string().describe("Human-readable flag name"),
163
+ description: z.string().optional().describe("Flag description"),
164
+ }, async ({ project_id, key, name, description }) => {
165
+ const body = { key, name };
166
+ if (description)
167
+ body.description = description;
168
+ const data = await client.request({
169
+ method: "POST",
170
+ path: `/flags/v1/projects/${project_id}/flags`,
171
+ body,
172
+ });
173
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
174
+ });
175
+ server.tool("update_flag", "Update an existing feature flag's name, description, or archived status.", {
176
+ project_id: z.number().describe("The project ID"),
177
+ flag_key: z.string().describe("The flag key to update"),
178
+ name: z.string().optional().describe("New flag name"),
179
+ description: z.string().optional().describe("New description"),
180
+ archived: z.boolean().optional().describe("Set to true to archive the flag"),
181
+ }, async ({ project_id, flag_key, ...updates }) => {
182
+ const body = {};
183
+ for (const [key, value] of Object.entries(updates)) {
184
+ if (value !== undefined)
185
+ body[key] = value;
186
+ }
187
+ const data = await client.request({
188
+ method: "PATCH",
189
+ path: `/flags/v1/projects/${project_id}/flags/${flag_key}`,
190
+ body,
191
+ });
192
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
193
+ });
194
+ // ─── Flag Rules (Targeting / Rollouts / A/B tests on flags) ─────────────────
195
+ server.tool("list_flag_rules", "List all rules (rollouts, A/B tests, targeted deliveries) for a flag in a specific environment.", {
196
+ project_id: z.number().describe("The project ID"),
197
+ flag_key: z.string().describe("The flag key"),
198
+ environment_key: z.string().describe("The environment key (e.g. 'production', 'development')"),
199
+ }, async ({ project_id, flag_key, environment_key }) => {
200
+ const data = await client.request({
201
+ path: `/flags/v1/projects/${project_id}/flags/${flag_key}/environments/${environment_key}/rules`,
202
+ });
203
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
204
+ });
205
+ server.tool("toggle_flag", "Enable or disable a feature flag in a specific environment. This controls whether the flag is active.", {
206
+ project_id: z.number().describe("The project ID"),
207
+ flag_key: z.string().describe("The flag key"),
208
+ environment_key: z.string().describe("The environment key"),
209
+ enabled: z.boolean().describe("true to enable, false to disable"),
210
+ }, async ({ project_id, flag_key, environment_key, enabled }) => {
211
+ const data = await client.request({
212
+ method: "PATCH",
213
+ path: `/flags/v1/projects/${project_id}/flags/${flag_key}/environments/${environment_key}/ruleset`,
214
+ body: { enabled },
215
+ });
216
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
217
+ });
218
+ // ─── Audiences ───────────────────────────────────────────────────────────────
219
+ server.tool("list_audiences", "List all audiences in a project. Audiences define targeting conditions for experiments.", {
220
+ project_id: z.number().describe("The project ID"),
221
+ page: z.number().optional(),
222
+ per_page: z.number().optional(),
223
+ }, async ({ project_id, page, per_page }) => {
224
+ const data = await client.request({
225
+ path: "/v2/audiences",
226
+ params: { project_id, page, per_page },
227
+ });
228
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
229
+ });
230
+ server.tool("get_audience", "Get details of a specific audience including its targeting conditions.", {
231
+ audience_id: z.number().describe("The audience ID"),
232
+ }, async ({ audience_id }) => {
233
+ const data = await client.request({
234
+ path: `/v2/audiences/${audience_id}`,
235
+ });
236
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
237
+ });
238
+ // ─── Events ──────────────────────────────────────────────────────────────────
239
+ server.tool("list_events", "List all tracked events/goals in a project. Events are used as metrics in experiments.", {
240
+ project_id: z.number().describe("The project ID"),
241
+ page: z.number().optional(),
242
+ per_page: z.number().optional(),
243
+ }, async ({ project_id, page, per_page }) => {
244
+ const data = await client.request({
245
+ path: "/v2/events",
246
+ params: { project_id, page, per_page },
247
+ });
248
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
249
+ });
250
+ // ─── Environments ────────────────────────────────────────────────────────────
251
+ server.tool("list_environments", "List all environments in a project (e.g. production, staging, development). Environments control where flags and experiments are active.", {
252
+ project_id: z.number().describe("The project ID"),
253
+ }, async ({ project_id }) => {
254
+ const data = await client.request({
255
+ path: `/v2/environments/${project_id}`,
256
+ });
257
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
258
+ });
259
+ // ─── Campaigns ───────────────────────────────────────────────────────────────
260
+ server.tool("list_campaigns", "List all campaigns (personalization campaigns or multi-page experiments) in a project.", {
261
+ project_id: z.number().describe("The project ID"),
262
+ page: z.number().optional(),
263
+ per_page: z.number().optional(),
264
+ }, async ({ project_id, page, per_page }) => {
265
+ const data = await client.request({
266
+ path: "/v2/campaigns",
267
+ params: { project_id, page, per_page },
268
+ });
269
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
270
+ });
271
+ server.tool("get_campaign_results", "Get results for a campaign including per-variation metrics, statistical significance, and improvement.", {
272
+ campaign_id: z.number().describe("The campaign ID"),
273
+ start_time: z.string().optional().describe("Start time (ISO 8601)"),
274
+ end_time: z.string().optional().describe("End time (ISO 8601)"),
275
+ }, async ({ campaign_id, start_time, end_time }) => {
276
+ const data = await client.request({
277
+ path: `/v2/campaigns/${campaign_id}/results`,
278
+ params: { start_time, end_time },
279
+ });
280
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
281
+ });
282
+ // ─── Attributes ──────────────────────────────────────────────────────────────
283
+ server.tool("list_attributes", "List all custom attributes defined in a project. Attributes are used for audience targeting.", {
284
+ project_id: z.number().describe("The project ID"),
285
+ page: z.number().optional(),
286
+ per_page: z.number().optional(),
287
+ }, async ({ project_id, page, per_page }) => {
288
+ const data = await client.request({
289
+ path: "/v2/attributes",
290
+ params: { project_id, page, per_page },
291
+ });
292
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
293
+ });
294
+ // ─── Pages ───────────────────────────────────────────────────────────────────
295
+ server.tool("list_pages", "List all pages defined in a project. Pages specify where experiments run (URL targeting).", {
296
+ project_id: z.number().describe("The project ID"),
297
+ page: z.number().optional(),
298
+ per_page: z.number().optional(),
299
+ }, async ({ project_id, page: pg, per_page }) => {
300
+ const data = await client.request({
301
+ path: "/v2/pages",
302
+ params: { project_id, page: pg, per_page },
303
+ });
304
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
305
+ });
306
+ // ─── Change History ──────────────────────────────────────────────────────────
307
+ server.tool("list_changes", "List recent changes/activity in a project. Shows who changed what and when.", {
308
+ project_id: z.number().describe("The project ID"),
309
+ page: z.number().optional(),
310
+ per_page: z.number().optional(),
311
+ }, async ({ project_id, page, per_page }) => {
312
+ const data = await client.request({
313
+ path: "/v2/changes",
314
+ params: { project_id, page, per_page },
315
+ });
316
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
317
+ });
318
+ // ─── Search ──────────────────────────────────────────────────────────────────
319
+ server.tool("search", "Search across all Optimizely resources (experiments, campaigns, pages, audiences, etc.) by keyword.", {
320
+ project_id: z.number().describe("The project ID to search in"),
321
+ query: z.string().describe("Search query string"),
322
+ per_page: z.number().optional().describe("Results per page"),
323
+ }, async ({ project_id, query, per_page }) => {
324
+ const data = await client.request({
325
+ path: "/v2/search",
326
+ params: { project_id, query, per_page },
327
+ });
328
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
329
+ });
330
+ // ─── Flag Variations ─────────────────────────────────────────────────────────
331
+ server.tool("list_flag_variations", "List all variations for a feature flag. Variations define different configurations of flag variables.", {
332
+ project_id: z.number().describe("The project ID"),
333
+ flag_key: z.string().describe("The flag key"),
334
+ }, async ({ project_id, flag_key }) => {
335
+ const data = await client.request({
336
+ path: `/flags/v1/projects/${project_id}/flags/${flag_key}/variations`,
337
+ });
338
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
339
+ });
340
+ server.tool("create_flag_variation", "Create a new variation for a feature flag with specific variable values.", {
341
+ project_id: z.number().describe("The project ID"),
342
+ flag_key: z.string().describe("The flag key"),
343
+ key: z.string().describe("Unique variation key"),
344
+ name: z.string().describe("Variation name"),
345
+ description: z.string().optional().describe("Variation description"),
346
+ variables: z
347
+ .record(z.any())
348
+ .optional()
349
+ .describe("Map of variable key to value"),
350
+ }, async ({ project_id, flag_key, key, name, description, variables }) => {
351
+ const body = { key, name };
352
+ if (description)
353
+ body.description = description;
354
+ if (variables)
355
+ body.variables = variables;
356
+ const data = await client.request({
357
+ method: "POST",
358
+ path: `/flags/v1/projects/${project_id}/flags/${flag_key}/variations`,
359
+ body,
360
+ });
361
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
362
+ });
363
+ // ─── Start the server ────────────────────────────────────────────────────────
364
+ async function main() {
365
+ const transport = new StdioServerTransport();
366
+ await server.connect(transport);
367
+ }
368
+ main().catch((err) => {
369
+ console.error("Fatal error:", err);
370
+ process.exit(1);
371
+ });
@@ -0,0 +1,11 @@
1
+ export interface OptimizelyRequestOptions {
2
+ method?: string;
3
+ path: string;
4
+ params?: Record<string, string | number | undefined>;
5
+ body?: unknown;
6
+ }
7
+ export declare class OptimizelyClient {
8
+ private token;
9
+ constructor(token: string);
10
+ request<T = unknown>(opts: OptimizelyRequestOptions): Promise<T>;
11
+ }
@@ -0,0 +1,34 @@
1
+ const BASE_URL = "https://api.optimizely.com";
2
+ export class OptimizelyClient {
3
+ token;
4
+ constructor(token) {
5
+ this.token = token;
6
+ }
7
+ async request(opts) {
8
+ const url = new URL(opts.path, BASE_URL);
9
+ if (opts.params) {
10
+ for (const [key, value] of Object.entries(opts.params)) {
11
+ if (value !== undefined) {
12
+ url.searchParams.set(key, String(value));
13
+ }
14
+ }
15
+ }
16
+ const headers = {
17
+ Authorization: `Bearer ${this.token}`,
18
+ "Content-Type": "application/json",
19
+ };
20
+ const response = await fetch(url.toString(), {
21
+ method: opts.method ?? "GET",
22
+ headers,
23
+ body: opts.body ? JSON.stringify(opts.body) : undefined,
24
+ });
25
+ if (response.status === 204) {
26
+ return {};
27
+ }
28
+ if (!response.ok) {
29
+ const errorBody = await response.text();
30
+ throw new Error(`Optimizely API error ${response.status}: ${errorBody}`);
31
+ }
32
+ return response.json();
33
+ }
34
+ }
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "optimizely-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for the Optimizely REST API — manage experiments, feature flags, audiences, and more from any MCP client.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "optimizely-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node dist/index.js",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "mcp",
21
+ "model-context-protocol",
22
+ "optimizely",
23
+ "feature-flags",
24
+ "experiments",
25
+ "a/b-testing",
26
+ "cursor"
27
+ ],
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.27.0",
31
+ "zod": "^3.23.0"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.5.0",
35
+ "@types/node": "^22.0.0"
36
+ }
37
+ }