@toggletown/mcp-server 0.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 +84 -0
- package/dist/bin/cli.js +5 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/src/api.js +86 -0
- package/dist/src/api.js.map +1 -0
- package/dist/src/index.js +19 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/resources/index.js +80 -0
- package/dist/src/resources/index.js.map +1 -0
- package/dist/src/server.js +28 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/tools/auditLogs.js +51 -0
- package/dist/src/tools/auditLogs.js.map +1 -0
- package/dist/src/tools/environments.js +45 -0
- package/dist/src/tools/environments.js.map +1 -0
- package/dist/src/tools/experiments.js +66 -0
- package/dist/src/tools/experiments.js.map +1 -0
- package/dist/src/tools/flags.js +121 -0
- package/dist/src/tools/flags.js.map +1 -0
- package/dist/src/tools/projects.js +46 -0
- package/dist/src/tools/projects.js.map +1 -0
- package/dist/src/tools/rules.js +73 -0
- package/dist/src/tools/rules.js.map +1 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @toggletown/mcp-server
|
|
2
|
+
|
|
3
|
+
MCP server for [ToggleTown](https://www.toggletown.com) — manage feature flags, rollouts, targeting rules, and A/B experiments from any AI assistant that supports the [Model Context Protocol](https://modelcontextprotocol.io).
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### Claude Desktop
|
|
8
|
+
|
|
9
|
+
Add to your `claude_desktop_config.json`:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"toggletown": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "@toggletown/mcp-server"],
|
|
17
|
+
"env": {
|
|
18
|
+
"TOGGLETOWN_API_KEY": "your-api-key"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Claude Code
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
claude mcp add toggletown -- npx -y @toggletown/mcp-server
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then set your API key in the environment:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export TOGGLETOWN_API_KEY=your-api-key
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Configuration
|
|
38
|
+
|
|
39
|
+
| Variable | Required | Description |
|
|
40
|
+
|----------|----------|-------------|
|
|
41
|
+
| `TOGGLETOWN_API_KEY` | Yes | Your account API key from the ToggleTown dashboard |
|
|
42
|
+
| `TOGGLETOWN_API_URL` | No | API base URL (defaults to `https://api.toggletown.com`) |
|
|
43
|
+
|
|
44
|
+
## Tools
|
|
45
|
+
|
|
46
|
+
The server exposes 12 tools:
|
|
47
|
+
|
|
48
|
+
| Tool | Description |
|
|
49
|
+
|------|-------------|
|
|
50
|
+
| `toggletown_list_projects` | List all projects with IDs, names, and flag counts |
|
|
51
|
+
| `toggletown_list_flags` | List feature flags in a project with per-environment status |
|
|
52
|
+
| `toggletown_get_flag` | Get full flag details including rules and rollout config |
|
|
53
|
+
| `toggletown_create_flag` | Create a new feature flag (boolean, string, number, or JSON) |
|
|
54
|
+
| `toggletown_toggle_flag` | Enable or disable a flag in a specific environment |
|
|
55
|
+
| `toggletown_evaluate_flag` | Test flag evaluation with a user context |
|
|
56
|
+
| `toggletown_update_rules` | Set targeting rules for a flag (first match wins) |
|
|
57
|
+
| `toggletown_set_rollout` | Set percentage rollout (deterministic user bucketing) |
|
|
58
|
+
| `toggletown_list_environments` | List environments in a project |
|
|
59
|
+
| `toggletown_get_audit_logs` | Get recent flag change activity |
|
|
60
|
+
| `toggletown_list_experiments` | List A/B experiments in a project |
|
|
61
|
+
| `toggletown_create_experiment` | Create a new A/B experiment on a string flag |
|
|
62
|
+
|
|
63
|
+
## Resources
|
|
64
|
+
|
|
65
|
+
The server also provides MCP resources for reading flag data:
|
|
66
|
+
|
|
67
|
+
- `toggletown://projects` — All projects overview
|
|
68
|
+
- `toggletown://projects/{projectId}/flags` — Flags for a project
|
|
69
|
+
- `toggletown://projects/{projectId}/environments` — Environments for a project
|
|
70
|
+
- `toggletown://projects/{projectId}/flags/{flagKey}` — Single flag detail
|
|
71
|
+
|
|
72
|
+
## Examples
|
|
73
|
+
|
|
74
|
+
Once connected, you can ask your AI assistant things like:
|
|
75
|
+
|
|
76
|
+
- "List all my feature flags"
|
|
77
|
+
- "Create a flag called `new-checkout` and roll it out to 10% in production"
|
|
78
|
+
- "Who changed the `dark-mode` flag recently?"
|
|
79
|
+
- "Set up a targeting rule so users with plan=enterprise get the new dashboard"
|
|
80
|
+
- "Create an A/B experiment on the hero-text flag with control and treatment variants"
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
package/dist/bin/cli.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";;;AACA,2BAAyB"}
|
package/dist/src/api.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listProjects = listProjects;
|
|
4
|
+
exports.getProject = getProject;
|
|
5
|
+
exports.listFlags = listFlags;
|
|
6
|
+
exports.getFlag = getFlag;
|
|
7
|
+
exports.createFlag = createFlag;
|
|
8
|
+
exports.toggleFlag = toggleFlag;
|
|
9
|
+
exports.updateFlagEnvironment = updateFlagEnvironment;
|
|
10
|
+
exports.evaluateFlag = evaluateFlag;
|
|
11
|
+
exports.listEnvironments = listEnvironments;
|
|
12
|
+
exports.getAuditLogs = getAuditLogs;
|
|
13
|
+
exports.listExperiments = listExperiments;
|
|
14
|
+
exports.createExperiment = createExperiment;
|
|
15
|
+
const baseUrl = process.env.TOGGLETOWN_API_URL || 'https://api.toggletown.com';
|
|
16
|
+
const apiKey = process.env.TOGGLETOWN_API_KEY || '';
|
|
17
|
+
async function request(method, path, body) {
|
|
18
|
+
const url = `${baseUrl}${path}`;
|
|
19
|
+
const res = await fetch(url, {
|
|
20
|
+
method,
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
'X-API-Key': apiKey,
|
|
24
|
+
},
|
|
25
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
26
|
+
});
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
const text = await res.text();
|
|
29
|
+
let message;
|
|
30
|
+
try {
|
|
31
|
+
const json = JSON.parse(text);
|
|
32
|
+
message = json.error?.message || json.message || text;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
message = text;
|
|
36
|
+
}
|
|
37
|
+
throw new Error(`API ${method} ${path} failed (${res.status}): ${message}`);
|
|
38
|
+
}
|
|
39
|
+
if (res.status === 204) {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
return res.json();
|
|
43
|
+
}
|
|
44
|
+
// Projects
|
|
45
|
+
function listProjects() {
|
|
46
|
+
return request('GET', '/api/v1/projects');
|
|
47
|
+
}
|
|
48
|
+
function getProject(projectId) {
|
|
49
|
+
return request('GET', `/api/v1/projects/${projectId}`);
|
|
50
|
+
}
|
|
51
|
+
// Flags
|
|
52
|
+
function listFlags(projectId) {
|
|
53
|
+
return request('GET', `/api/v1/projects/${projectId}/flags`);
|
|
54
|
+
}
|
|
55
|
+
function getFlag(projectId, flagKey) {
|
|
56
|
+
return request('GET', `/api/v1/projects/${projectId}/flags/${flagKey}`);
|
|
57
|
+
}
|
|
58
|
+
function createFlag(projectId, data) {
|
|
59
|
+
return request('POST', `/api/v1/projects/${projectId}/flags`, data);
|
|
60
|
+
}
|
|
61
|
+
function toggleFlag(projectId, flagKey, environmentId, enabled) {
|
|
62
|
+
return request('PUT', `/api/v1/projects/${projectId}/flags/${flagKey}/environments/${environmentId}`, { enabled });
|
|
63
|
+
}
|
|
64
|
+
function updateFlagEnvironment(projectId, flagKey, environmentId, data) {
|
|
65
|
+
return request('PUT', `/api/v1/projects/${projectId}/flags/${flagKey}/environments/${environmentId}`, data);
|
|
66
|
+
}
|
|
67
|
+
function evaluateFlag(projectId, flagKey, environmentId, context) {
|
|
68
|
+
return request('POST', `/api/v1/projects/${projectId}/flags/${flagKey}/evaluate/${environmentId}`, context);
|
|
69
|
+
}
|
|
70
|
+
// Environments
|
|
71
|
+
function listEnvironments(projectId) {
|
|
72
|
+
return request('GET', `/api/v1/projects/${projectId}/environments`);
|
|
73
|
+
}
|
|
74
|
+
// Audit Logs
|
|
75
|
+
function getAuditLogs(projectId, params) {
|
|
76
|
+
const query = params?.limit ? `?limit=${params.limit}` : '';
|
|
77
|
+
return request('GET', `/api/v1/projects/${projectId}/audit-logs/activity${query}`);
|
|
78
|
+
}
|
|
79
|
+
// Experiments
|
|
80
|
+
function listExperiments(projectId) {
|
|
81
|
+
return request('GET', `/api/v1/projects/${projectId}/experiments`);
|
|
82
|
+
}
|
|
83
|
+
function createExperiment(projectId, data) {
|
|
84
|
+
return request('POST', `/api/v1/projects/${projectId}/experiments`, data);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
4
|
+
const server_js_1 = require("./server.js");
|
|
5
|
+
async function main() {
|
|
6
|
+
if (!process.env.TOGGLETOWN_API_KEY) {
|
|
7
|
+
console.error('Error: TOGGLETOWN_API_KEY environment variable is required.');
|
|
8
|
+
console.error('Set it to your account API key (acc_...) from the ToggleTown dashboard.');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const server = (0, server_js_1.createServer)();
|
|
12
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
13
|
+
await server.connect(transport);
|
|
14
|
+
}
|
|
15
|
+
main().catch((error) => {
|
|
16
|
+
console.error('Fatal error:', error);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;AAAA,wEAAiF;AACjF,2CAA2C;AAE3C,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC;QACpC,OAAO,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;QAC7E,OAAO,CAAC,KAAK,CAAC,yEAAyE,CAAC,CAAC;QACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,wBAAY,GAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.registerResources = registerResources;
|
|
27
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
28
|
+
const api = __importStar(require("../api.js"));
|
|
29
|
+
function registerResources(server) {
|
|
30
|
+
// Static: all projects overview
|
|
31
|
+
server.registerResource('projects-list', 'toggletown://projects', { title: 'ToggleTown Projects', description: 'All projects overview', mimeType: 'application/json' }, async (uri) => {
|
|
32
|
+
const data = await api.listProjects();
|
|
33
|
+
const projects = data.projects.map((p) => ({
|
|
34
|
+
id: p.id,
|
|
35
|
+
name: p.name,
|
|
36
|
+
flagCount: p._count?.flags ?? 0,
|
|
37
|
+
environments: p.environments?.map((e) => ({ id: e.id, name: e.name })) ?? [],
|
|
38
|
+
}));
|
|
39
|
+
return {
|
|
40
|
+
contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(projects, null, 2) }],
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
// Dynamic: flags for a project
|
|
44
|
+
server.registerResource('project-flags', new mcp_js_1.ResourceTemplate('toggletown://projects/{projectId}/flags', { list: undefined }), { title: 'Project Flags', description: 'Feature flags for a project', mimeType: 'application/json' }, async (uri, variables) => {
|
|
45
|
+
const projectId = String(variables.projectId);
|
|
46
|
+
const data = await api.listFlags(projectId);
|
|
47
|
+
const flags = data.flags.map((f) => ({
|
|
48
|
+
key: f.key,
|
|
49
|
+
name: f.name,
|
|
50
|
+
type: f.type,
|
|
51
|
+
environments: f.flagEnvironments?.map((fe) => ({
|
|
52
|
+
environmentName: fe.environment?.name,
|
|
53
|
+
enabled: fe.enabled,
|
|
54
|
+
rolloutPercentage: fe.rolloutPercentage,
|
|
55
|
+
})) ?? [],
|
|
56
|
+
}));
|
|
57
|
+
return {
|
|
58
|
+
contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(flags, null, 2) }],
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
// Dynamic: environments for a project
|
|
62
|
+
server.registerResource('project-environments', new mcp_js_1.ResourceTemplate('toggletown://projects/{projectId}/environments', { list: undefined }), { title: 'Project Environments', description: 'Environments for a project', mimeType: 'application/json' }, async (uri, variables) => {
|
|
63
|
+
const projectId = String(variables.projectId);
|
|
64
|
+
const data = await api.listEnvironments(projectId);
|
|
65
|
+
const environments = data.environments.map((e) => ({ id: e.id, name: e.name }));
|
|
66
|
+
return {
|
|
67
|
+
contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(environments, null, 2) }],
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
// Dynamic: single flag detail
|
|
71
|
+
server.registerResource('flag-detail', new mcp_js_1.ResourceTemplate('toggletown://projects/{projectId}/flags/{flagKey}', { list: undefined }), { title: 'Flag Detail', description: 'Full details of a feature flag', mimeType: 'application/json' }, async (uri, variables) => {
|
|
72
|
+
const projectId = String(variables.projectId);
|
|
73
|
+
const flagKey = String(variables.flagKey);
|
|
74
|
+
const data = await api.getFlag(projectId, flagKey);
|
|
75
|
+
return {
|
|
76
|
+
contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(data.flag, null, 2) }],
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/resources/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createServer = createServer;
|
|
4
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
|
+
const projects_js_1 = require("./tools/projects.js");
|
|
6
|
+
const flags_js_1 = require("./tools/flags.js");
|
|
7
|
+
const rules_js_1 = require("./tools/rules.js");
|
|
8
|
+
const environments_js_1 = require("./tools/environments.js");
|
|
9
|
+
const auditLogs_js_1 = require("./tools/auditLogs.js");
|
|
10
|
+
const experiments_js_1 = require("./tools/experiments.js");
|
|
11
|
+
const index_js_1 = require("./resources/index.js");
|
|
12
|
+
function createServer() {
|
|
13
|
+
const server = new mcp_js_1.McpServer({
|
|
14
|
+
name: 'toggletown',
|
|
15
|
+
version: '0.1.0',
|
|
16
|
+
});
|
|
17
|
+
// Register all tools (12 total)
|
|
18
|
+
(0, projects_js_1.registerProjectTools)(server);
|
|
19
|
+
(0, flags_js_1.registerFlagTools)(server);
|
|
20
|
+
(0, rules_js_1.registerRuleTools)(server);
|
|
21
|
+
(0, environments_js_1.registerEnvironmentTools)(server);
|
|
22
|
+
(0, auditLogs_js_1.registerAuditLogTools)(server);
|
|
23
|
+
(0, experiments_js_1.registerExperimentTools)(server);
|
|
24
|
+
// Register resources (4 total)
|
|
25
|
+
(0, index_js_1.registerResources)(server);
|
|
26
|
+
return server;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":";;AASA,oCAkBC;AA3BD,oEAAoE;AACpE,qDAA2D;AAC3D,+CAAqD;AACrD,+CAAqD;AACrD,6DAAmE;AACnE,uDAA6D;AAC7D,2DAAiE;AACjE,mDAAyD;AAEzD,SAAgB,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,kBAAS,CAAC;QAC3B,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,gCAAgC;IAChC,IAAA,kCAAoB,EAAC,MAAM,CAAC,CAAC;IAC7B,IAAA,4BAAiB,EAAC,MAAM,CAAC,CAAC;IAC1B,IAAA,4BAAiB,EAAC,MAAM,CAAC,CAAC;IAC1B,IAAA,0CAAwB,EAAC,MAAM,CAAC,CAAC;IACjC,IAAA,oCAAqB,EAAC,MAAM,CAAC,CAAC;IAC9B,IAAA,wCAAuB,EAAC,MAAM,CAAC,CAAC;IAEhC,+BAA+B;IAC/B,IAAA,4BAAiB,EAAC,MAAM,CAAC,CAAC;IAE1B,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.registerAuditLogTools = registerAuditLogTools;
|
|
27
|
+
const zod_1 = require("zod");
|
|
28
|
+
const api = __importStar(require("../api.js"));
|
|
29
|
+
function registerAuditLogTools(server) {
|
|
30
|
+
server.registerTool('toggletown_get_audit_logs', {
|
|
31
|
+
title: 'Get Audit Logs',
|
|
32
|
+
description: 'Get recent flag change activity for a project (who changed what, when)',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
35
|
+
limit: zod_1.z.number().min(1).max(100).default(15).describe('Number of entries to return'),
|
|
36
|
+
},
|
|
37
|
+
}, async ({ projectId, limit }) => {
|
|
38
|
+
const data = await api.getAuditLogs(projectId, { limit });
|
|
39
|
+
const activity = data.activity.map((a) => ({
|
|
40
|
+
action: a.action,
|
|
41
|
+
resourceType: a.resourceType,
|
|
42
|
+
user: a.user?.email,
|
|
43
|
+
changes: a.changes,
|
|
44
|
+
timestamp: a.createdAt,
|
|
45
|
+
}));
|
|
46
|
+
return {
|
|
47
|
+
content: [{ type: 'text', text: JSON.stringify(activity, null, 2) }],
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=auditLogs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditLogs.js","sourceRoot":"","sources":["../../../src/tools/auditLogs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAIA,sDAyBC;AA5BD,6BAAwB;AACxB,+CAAiC;AAEjC,SAAgB,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,YAAY,CACjB,2BAA2B,EAC3B;QACE,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,wEAAwE;QACrF,WAAW,EAAE;YACX,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;YAC5C,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,6BAA6B,CAAC;SACtF;KACF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAwC,EAAE,EAAE;QACnE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,YAAY,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC9C,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,YAAY,EAAE,CAAC,CAAC,YAAY;YAC5B,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK;YACnB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,SAAS,EAAE,CAAC,CAAC,SAAS;SACvB,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC9E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.registerEnvironmentTools = registerEnvironmentTools;
|
|
27
|
+
const zod_1 = require("zod");
|
|
28
|
+
const api = __importStar(require("../api.js"));
|
|
29
|
+
function registerEnvironmentTools(server) {
|
|
30
|
+
server.registerTool('toggletown_list_environments', {
|
|
31
|
+
title: 'List Environments',
|
|
32
|
+
description: 'List all environments in a project with their names and IDs',
|
|
33
|
+
inputSchema: { projectId: zod_1.z.string().describe('Project ID') },
|
|
34
|
+
}, async ({ projectId }) => {
|
|
35
|
+
const data = await api.listEnvironments(projectId);
|
|
36
|
+
const environments = data.environments.map((e) => ({
|
|
37
|
+
id: e.id,
|
|
38
|
+
name: e.name,
|
|
39
|
+
}));
|
|
40
|
+
return {
|
|
41
|
+
content: [{ type: 'text', text: JSON.stringify(environments, null, 2) }],
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=environments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environments.js","sourceRoot":"","sources":["../../../src/tools/environments.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAIA,4DAmBC;AAtBD,6BAAwB;AACxB,+CAAiC;AAEjC,SAAgB,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,YAAY,CACjB,8BAA8B,EAC9B;QACE,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,6DAA6D;QAC1E,WAAW,EAAE,EAAE,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE;KAC9D,EACD,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,EAAE;QAC7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YACtD,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,IAAI;SACb,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAClF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.registerExperimentTools = registerExperimentTools;
|
|
27
|
+
const zod_1 = require("zod");
|
|
28
|
+
const api = __importStar(require("../api.js"));
|
|
29
|
+
function registerExperimentTools(server) {
|
|
30
|
+
server.registerTool('toggletown_list_experiments', {
|
|
31
|
+
title: 'List Experiments',
|
|
32
|
+
description: 'List all A/B experiments in a project',
|
|
33
|
+
inputSchema: { projectId: zod_1.z.string().describe('Project ID') },
|
|
34
|
+
}, async ({ projectId }) => {
|
|
35
|
+
const data = await api.listExperiments(projectId);
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: 'text', text: JSON.stringify(data.experiments, null, 2) }],
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
server.registerTool('toggletown_create_experiment', {
|
|
41
|
+
title: 'Create Experiment',
|
|
42
|
+
description: 'Create a new A/B experiment linked to a STRING feature flag',
|
|
43
|
+
inputSchema: {
|
|
44
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
45
|
+
name: zod_1.z.string().describe('Experiment name'),
|
|
46
|
+
flagId: zod_1.z.string().describe('ID of the STRING flag to experiment on'),
|
|
47
|
+
environmentId: zod_1.z.string().describe('Environment ID to run the experiment in'),
|
|
48
|
+
hypothesis: zod_1.z.string().optional().describe('What you expect to happen'),
|
|
49
|
+
primaryMetric: zod_1.z.string().describe('Primary metric to measure (e.g. "conversion_rate")'),
|
|
50
|
+
variants: zod_1.z.array(zod_1.z.object({
|
|
51
|
+
name: zod_1.z.string().describe('Variant name (e.g. "control", "treatment")'),
|
|
52
|
+
value: zod_1.z.string().describe('Flag value for this variant'),
|
|
53
|
+
trafficAllocation: zod_1.z.number().min(0).max(100).describe('Traffic percentage for this variant'),
|
|
54
|
+
})).min(2).max(10).describe('Experiment variants (traffic allocations must sum to 100)'),
|
|
55
|
+
},
|
|
56
|
+
}, async ({ projectId, name, flagId, environmentId, hypothesis, primaryMetric, variants }) => {
|
|
57
|
+
const data = await api.createExperiment(projectId, { name, flagId, environmentId, hypothesis, primaryMetric, variants });
|
|
58
|
+
return {
|
|
59
|
+
content: [{
|
|
60
|
+
type: 'text',
|
|
61
|
+
text: `Experiment "${name}" created.\n\n${JSON.stringify(data.experiment, null, 2)}`,
|
|
62
|
+
}],
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=experiments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experiments.js","sourceRoot":"","sources":["../../../src/tools/experiments.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAIA,0DA6CC;AAhDD,6BAAwB;AACxB,+CAAiC;AAEjC,SAAgB,uBAAuB,CAAC,MAAiB;IACvD,MAAM,CAAC,YAAY,CACjB,6BAA6B,EAC7B;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,uCAAuC;QACpD,WAAW,EAAE,EAAE,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE;KAC9D,EACD,KAAK,EAAE,EAAE,SAAS,EAAyB,EAAE,EAAE;QAC7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QAClD,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SACtF,CAAC;IACJ,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,8BAA8B,EAC9B;QACE,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,6DAA6D;QAC1E,WAAW,EAAE;YACX,SAAS,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;YAC5C,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;YAC5C,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;YACrE,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;YAC7E,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;YACvE,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oDAAoD,CAAC;YACxF,QAAQ,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,CAAC;gBACzB,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;gBACvE,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;gBACzD,iBAAiB,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,qCAAqC,CAAC;aAC9F,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,2DAA2D,CAAC;SACzF;KACF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAuM,EAAE,EAAE;QAC7R,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC;QACzH,OAAO;YACL,OAAO,EAAE,CAAC;oBACR,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE,eAAe,IAAI,iBAAiB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBACrF,CAAC;SACH,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.registerFlagTools = registerFlagTools;
|
|
27
|
+
const zod_1 = require("zod");
|
|
28
|
+
const api = __importStar(require("../api.js"));
|
|
29
|
+
function registerFlagTools(server) {
|
|
30
|
+
server.registerTool('toggletown_list_flags', {
|
|
31
|
+
title: 'List Flags',
|
|
32
|
+
description: 'List all feature flags in a project with their enabled status per environment',
|
|
33
|
+
inputSchema: { projectId: zod_1.z.string().describe('Project ID') },
|
|
34
|
+
}, async ({ projectId }) => {
|
|
35
|
+
const data = await api.listFlags(projectId);
|
|
36
|
+
const flags = data.flags.map((f) => ({
|
|
37
|
+
key: f.key,
|
|
38
|
+
name: f.name,
|
|
39
|
+
type: f.type,
|
|
40
|
+
description: f.description,
|
|
41
|
+
environments: f.flagEnvironments?.map((fe) => ({
|
|
42
|
+
environmentId: fe.environmentId,
|
|
43
|
+
environmentName: fe.environment?.name,
|
|
44
|
+
enabled: fe.enabled,
|
|
45
|
+
rolloutPercentage: fe.rolloutPercentage,
|
|
46
|
+
})) ?? [],
|
|
47
|
+
}));
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: 'text', text: JSON.stringify(flags, null, 2) }],
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
server.registerTool('toggletown_get_flag', {
|
|
53
|
+
title: 'Get Flag',
|
|
54
|
+
description: 'Get full details of a feature flag including rules, rollout, and environment configs',
|
|
55
|
+
inputSchema: {
|
|
56
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
57
|
+
flagKey: zod_1.z.string().describe('Flag key (e.g. "dark-mode")'),
|
|
58
|
+
},
|
|
59
|
+
}, async ({ projectId, flagKey }) => {
|
|
60
|
+
const data = await api.getFlag(projectId, flagKey);
|
|
61
|
+
return {
|
|
62
|
+
content: [{ type: 'text', text: JSON.stringify(data.flag, null, 2) }],
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
server.registerTool('toggletown_create_flag', {
|
|
66
|
+
title: 'Create Flag',
|
|
67
|
+
description: 'Create a new feature flag in a project',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
70
|
+
key: zod_1.z.string().describe('Flag key in kebab-case (e.g. "new-checkout")'),
|
|
71
|
+
name: zod_1.z.string().describe('Human-readable flag name'),
|
|
72
|
+
type: zod_1.z.enum(['BOOLEAN', 'STRING', 'NUMBER', 'JSON']).default('BOOLEAN').describe('Flag type'),
|
|
73
|
+
description: zod_1.z.string().optional().describe('Flag description'),
|
|
74
|
+
},
|
|
75
|
+
}, async ({ projectId, key, name, type, description }) => {
|
|
76
|
+
const data = await api.createFlag(projectId, { key, name, type, description });
|
|
77
|
+
return {
|
|
78
|
+
content: [{ type: 'text', text: `Flag "${key}" created.\n\n${JSON.stringify(data.flag, null, 2)}` }],
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
server.registerTool('toggletown_toggle_flag', {
|
|
82
|
+
title: 'Toggle Flag',
|
|
83
|
+
description: 'Enable or disable a feature flag in a specific environment',
|
|
84
|
+
inputSchema: {
|
|
85
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
86
|
+
flagKey: zod_1.z.string().describe('Flag key'),
|
|
87
|
+
environmentId: zod_1.z.string().describe('Environment ID'),
|
|
88
|
+
enabled: zod_1.z.boolean().describe('Whether the flag should be enabled'),
|
|
89
|
+
},
|
|
90
|
+
}, async ({ projectId, flagKey, environmentId, enabled }) => {
|
|
91
|
+
const data = await api.toggleFlag(projectId, flagKey, environmentId, enabled);
|
|
92
|
+
return {
|
|
93
|
+
content: [{
|
|
94
|
+
type: 'text',
|
|
95
|
+
text: `Flag "${flagKey}" is now ${enabled ? 'ENABLED' : 'DISABLED'} in ${data.flagEnvironment?.environment?.name || environmentId}.\n\n${JSON.stringify(data.flagEnvironment, null, 2)}`,
|
|
96
|
+
}],
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
server.registerTool('toggletown_evaluate_flag', {
|
|
100
|
+
title: 'Evaluate Flag',
|
|
101
|
+
description: 'Test flag evaluation with a user context to preview what value would be returned',
|
|
102
|
+
inputSchema: {
|
|
103
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
104
|
+
flagKey: zod_1.z.string().describe('Flag key'),
|
|
105
|
+
environmentId: zod_1.z.string().describe('Environment ID'),
|
|
106
|
+
context: zod_1.z.object({
|
|
107
|
+
userId: zod_1.z.string().optional().describe('User ID for rollout bucketing'),
|
|
108
|
+
attributes: zod_1.z.record(zod_1.z.any()).optional().describe('User attributes for targeting rules'),
|
|
109
|
+
}).describe('Evaluation context'),
|
|
110
|
+
},
|
|
111
|
+
}, async ({ projectId, flagKey, environmentId, context }) => {
|
|
112
|
+
const data = await api.evaluateFlag(projectId, flagKey, environmentId, context);
|
|
113
|
+
return {
|
|
114
|
+
content: [{
|
|
115
|
+
type: 'text',
|
|
116
|
+
text: `Flag "${flagKey}" evaluates to: ${JSON.stringify(data.value)}\n\nReason: ${data.debug?.reason}\n\nFull debug:\n${JSON.stringify(data.debug, null, 2)}`,
|
|
117
|
+
}],
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=flags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flags.js","sourceRoot":"","sources":["../../../src/tools/flags.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.registerProjectTools = registerProjectTools;
|
|
27
|
+
const api = __importStar(require("../api.js"));
|
|
28
|
+
function registerProjectTools(server) {
|
|
29
|
+
server.registerTool('toggletown_list_projects', {
|
|
30
|
+
title: 'List Projects',
|
|
31
|
+
description: 'List all ToggleTown projects with their IDs, names, and flag counts',
|
|
32
|
+
}, async () => {
|
|
33
|
+
const data = await api.listProjects();
|
|
34
|
+
const projects = data.projects.map((p) => ({
|
|
35
|
+
id: p.id,
|
|
36
|
+
name: p.name,
|
|
37
|
+
flagCount: p._count?.flags ?? 0,
|
|
38
|
+
role: p.myRole,
|
|
39
|
+
environments: p.environments?.map((e) => ({ id: e.id, name: e.name })) ?? [],
|
|
40
|
+
}));
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }],
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../../src/tools/projects.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAGA,oDAqBC;AAvBD,+CAAiC;AAEjC,SAAgB,oBAAoB,CAAC,MAAiB;IACpD,MAAM,CAAC,YAAY,CACjB,0BAA0B,EAC1B;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,qEAAqE;KACnF,EACD,KAAK,IAAI,EAAE;QACT,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,YAAY,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC9C,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC;YAC/B,IAAI,EAAE,CAAC,CAAC,MAAM;YACd,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;SAClF,CAAC,CAAC,CAAC;QACJ,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC9E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.registerRuleTools = registerRuleTools;
|
|
27
|
+
const zod_1 = require("zod");
|
|
28
|
+
const api = __importStar(require("../api.js"));
|
|
29
|
+
function registerRuleTools(server) {
|
|
30
|
+
server.registerTool('toggletown_update_rules', {
|
|
31
|
+
title: 'Update Rules',
|
|
32
|
+
description: 'Set targeting rules for a feature flag in a specific environment. Rules are evaluated in order; first match wins.',
|
|
33
|
+
inputSchema: {
|
|
34
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
35
|
+
flagKey: zod_1.z.string().describe('Flag key'),
|
|
36
|
+
environmentId: zod_1.z.string().describe('Environment ID'),
|
|
37
|
+
rules: zod_1.z.array(zod_1.z.object({
|
|
38
|
+
attribute: zod_1.z.string().describe('User attribute to match (e.g. "email", "country", "plan")'),
|
|
39
|
+
operator: zod_1.z.enum(['equals', 'not_equals', 'contains', 'not_contains', 'gt', 'lt', 'in', 'not_in', 'in_segment', 'always']).describe('Comparison operator'),
|
|
40
|
+
value: zod_1.z.any().describe('Value to compare against'),
|
|
41
|
+
rollValue: zod_1.z.any().optional().describe('Value to return when rule matches (for non-boolean flags)'),
|
|
42
|
+
percentage: zod_1.z.number().min(0).max(100).optional().describe('Percentage rollout for this rule (0-100)'),
|
|
43
|
+
})).describe('Ordered list of targeting rules'),
|
|
44
|
+
},
|
|
45
|
+
}, async ({ projectId, flagKey, environmentId, rules }) => {
|
|
46
|
+
const data = await api.updateFlagEnvironment(projectId, flagKey, environmentId, { rules });
|
|
47
|
+
return {
|
|
48
|
+
content: [{
|
|
49
|
+
type: 'text',
|
|
50
|
+
text: `Updated ${rules.length} targeting rule(s) for "${flagKey}".\n\n${JSON.stringify(data.flagEnvironment, null, 2)}`,
|
|
51
|
+
}],
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
server.registerTool('toggletown_set_rollout', {
|
|
55
|
+
title: 'Set Rollout',
|
|
56
|
+
description: 'Set the percentage rollout for a feature flag (0-100). Users are deterministically bucketed by their userId.',
|
|
57
|
+
inputSchema: {
|
|
58
|
+
projectId: zod_1.z.string().describe('Project ID'),
|
|
59
|
+
flagKey: zod_1.z.string().describe('Flag key'),
|
|
60
|
+
environmentId: zod_1.z.string().describe('Environment ID'),
|
|
61
|
+
percentage: zod_1.z.number().min(0).max(100).describe('Rollout percentage (0 = off for all, 100 = on for all)'),
|
|
62
|
+
},
|
|
63
|
+
}, async ({ projectId, flagKey, environmentId, percentage }) => {
|
|
64
|
+
const data = await api.updateFlagEnvironment(projectId, flagKey, environmentId, { rolloutPercentage: percentage });
|
|
65
|
+
return {
|
|
66
|
+
content: [{
|
|
67
|
+
type: 'text',
|
|
68
|
+
text: `Rollout for "${flagKey}" set to ${percentage}%.\n\n${JSON.stringify(data.flagEnvironment, null, 2)}`,
|
|
69
|
+
}],
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../src/tools/rules.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toggletown/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ToggleTown MCP server — manage feature flags, rollouts, targeting rules, and A/B experiments from any AI assistant",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "ToggleTown <support@toggletown.com>",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/toggletown/toggletown.git",
|
|
10
|
+
"directory": "packages/mcp-server"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://docs.toggletown.com/mcp",
|
|
13
|
+
"bugs": "https://github.com/toggletown/toggletown/issues",
|
|
14
|
+
"main": "dist/src/index.js",
|
|
15
|
+
"bin": {
|
|
16
|
+
"toggletown-mcp": "./dist/bin/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc --noCheck",
|
|
20
|
+
"dev": "tsc -w --noCheck",
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"clean": "rm -rf dist"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"keywords": [
|
|
29
|
+
"feature-flags",
|
|
30
|
+
"feature-toggles",
|
|
31
|
+
"toggletown",
|
|
32
|
+
"mcp",
|
|
33
|
+
"mcp-server",
|
|
34
|
+
"model-context-protocol",
|
|
35
|
+
"ai",
|
|
36
|
+
"claude",
|
|
37
|
+
"llm",
|
|
38
|
+
"ab-testing",
|
|
39
|
+
"rollout",
|
|
40
|
+
"targeting"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
47
|
+
"zod": "^3.23.0"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^20.11.0",
|
|
51
|
+
"typescript": "^5.3.3"
|
|
52
|
+
}
|
|
53
|
+
}
|