Skip to main content
Glama

Flipt MCP Server

Official
by flipt-io
index.ts29.8 kB
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { z } from 'zod'; import dotenv from 'dotenv'; import { FliptClient } from './services/fliptClient'; import { VERSION } from './version'; dotenv.config(); const fliptClient = new FliptClient(); const server = new McpServer( { name: 'Flipt MCP Server', version: VERSION, }, { capabilities: { tools: {}, }, } ); // Namespace tools server.tool('list_namespaces', {}, async args => { const namespaces = await fliptClient.listNamespaces(); return { content: [ { type: 'text', text: JSON.stringify(namespaces, null, 2), }, ], }; }); server.tool( 'create_namespace', { key: z.string().min(1), name: z.string().min(1), description: z.string().optional(), }, async args => { try { const namespace = await fliptClient.createNamespace(args.key, args.name, args.description); return { content: [ { type: 'text', text: JSON.stringify(namespace, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.key}`, }, }; } catch (error: any) { console.error('Error creating namespace:', error); return { content: [ { type: 'text', text: `Failed to create namespace: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'update_namespace', { key: z.string().min(1), name: z.string().min(1), description: z.string().optional(), }, async args => { try { const currentNamespace = await fliptClient.getNamespace(args.key); if (!currentNamespace) { return { content: [ { type: 'text', text: `Namespace ${args.key} not found`, }, ], isError: true, }; } // update changed fields const updatedNamespace = { ...currentNamespace, name: args.name || currentNamespace.name, description: args.description || currentNamespace.description, }; const namespace = await fliptClient.updateNamespace( currentNamespace.key!, updatedNamespace.name!, updatedNamespace.description ); return { content: [ { type: 'text', text: JSON.stringify(namespace, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.key}`, }, }; } catch (error: any) { console.error('Error updating namespace:', error); return { content: [ { type: 'text', text: `Failed to update namespace: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'delete_namespace', { key: z.string().min(1), }, async args => { try { await fliptClient.deleteNamespace(args.key); return { content: [ { type: 'text', text: `Deleted namespace ${args.key}`, }, ], }; } catch (error: any) { console.error('Error deleting namespace:', error); return { content: [ { type: 'text', text: `Failed to delete namespace: ${error.message}`, }, ], isError: true, }; } } ); // Flag tools server.tool( 'list_flags', { namespaceKey: z.string().min(1), }, async args => { try { const flags = await fliptClient.listFlags(args.namespaceKey); return { content: [ { type: 'text', text: JSON.stringify(flags, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.namespaceKey}/flags`, }, }; } catch (error: any) { console.error(`Error listing flags for namespace ${args.namespaceKey}:`, error); return { content: [ { type: 'text', text: `Failed to list flags: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'get_flag', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), }, async args => { try { const flag = await fliptClient.getFlag(args.namespaceKey, args.flagKey); return { content: [ { type: 'text', text: JSON.stringify(flag, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.namespaceKey}/flags/${args.flagKey}`, }, }; } catch (error: any) { console.error(`Error getting flag ${args.flagKey} in namespace ${args.namespaceKey}:`, error); return { content: [ { type: 'text', text: `Failed to get flag: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'create_flag', { namespaceKey: z.string().min(1), key: z.string().min(1), name: z.string().min(1), description: z.string().optional(), enabled: z.boolean().optional(), type: z.enum(['VARIANT_FLAG_TYPE', 'BOOLEAN_FLAG_TYPE']), }, async args => { try { const flag = await fliptClient.createFlag( args.namespaceKey, args.key, args.name, args.description, args.enabled, args.type ); return { content: [ { type: 'text', text: JSON.stringify(flag, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.namespaceKey}/flags/${args.key}`, }, }; } catch (error: any) { console.error('Error creating flag:', error); return { content: [ { type: 'text', text: `Failed to create flag: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'update_flag', { namespaceKey: z.string().min(1), key: z.string().min(1), name: z.string().min(1), description: z.string().optional(), enabled: z.boolean().optional(), }, async args => { try { const currentFlag = await fliptClient.getFlag(args.namespaceKey, args.key); if (!currentFlag) { return { content: [ { type: 'text', text: `Flag ${args.key} in namespace ${args.namespaceKey} not found`, }, ], isError: true, }; } // update changed fields const updatedFlag = { ...currentFlag, name: args.name || currentFlag.name, description: args.description || currentFlag.description, enabled: args.enabled || currentFlag.enabled, }; const flag = await fliptClient.updateFlag( currentFlag.namespaceKey!, currentFlag.key!, updatedFlag.name!, updatedFlag.description, updatedFlag.enabled ); return { content: [ { type: 'text', text: JSON.stringify(flag, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.namespaceKey}/flags/${args.key}`, }, }; } catch (error: any) { console.error('Error updating flag:', error); return { content: [ { type: 'text', text: `Failed to update flag: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'delete_flag', { namespaceKey: z.string().min(1), key: z.string().min(1), }, async args => { try { await fliptClient.deleteFlag(args.namespaceKey, args.key); return { content: [ { type: 'text', text: `Deleted flag ${args.key} in namespace ${args.namespaceKey}`, }, ], }; } catch (error: any) { console.error('Error deleting flag:', error); return { content: [ { type: 'text', text: `Failed to delete flag: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'toggle_flag', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), enabled: z.boolean(), }, async args => { try { const currentFlag = await fliptClient.getFlag(args.namespaceKey, args.flagKey); await fliptClient.updateFlag( args.namespaceKey, args.flagKey, currentFlag.name!, currentFlag.description, args.enabled ); return { content: [ { type: 'text', text: `Flag ${args.flagKey} in namespace ${args.namespaceKey} is now ${args.enabled ? 'enabled' : 'disabled'}`, }, ], _meta: { uri: `flipt://namespaces/${args.namespaceKey}/flags/${args.flagKey}`, }, }; } catch (error: any) { console.error('Error toggling flag:', error); return { content: [ { type: 'text', text: `Failed to toggle flag: ${error.message}`, }, ], isError: true, }; } } ); // Segment tools server.tool( 'list_segments', { namespaceKey: z.string().min(1), }, async args => { try { const segments = await fliptClient.listSegments(args.namespaceKey); return { content: [ { type: 'text', text: JSON.stringify(segments, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.namespaceKey}/segments`, }, }; } catch (error: any) { console.error(`Error listing segments for namespace ${args.namespaceKey}:`, error); return { content: [ { type: 'text', text: `Failed to list segments: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'get_segment', { namespaceKey: z.string().min(1), segmentKey: z.string().min(1), }, async args => { try { const segment = await fliptClient.getSegment(args.namespaceKey, args.segmentKey); return { content: [ { type: 'text', text: JSON.stringify(segment, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.namespaceKey}/segments/${args.segmentKey}`, }, }; } catch (error: any) { console.error( `Error getting segment ${args.segmentKey} in namespace ${args.namespaceKey}:`, error ); return { content: [ { type: 'text', text: `Failed to get segment: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'create_segment', { namespaceKey: z.string().min(1), key: z.string().min(1), name: z.string().min(1), description: z.string().optional(), matchType: z.enum(['ALL_MATCH_TYPE', 'ANY_MATCH_TYPE']), }, async args => { try { const segment = await fliptClient.createSegment( args.namespaceKey, args.key, args.name, args.description, args.matchType ); return { content: [ { type: 'text', text: JSON.stringify(segment, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.namespaceKey}/segments/${args.key}`, }, }; } catch (error: any) { console.error('Error creating segment:', error); return { content: [ { type: 'text', text: `Failed to create segment: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'update_segment', { namespaceKey: z.string().min(1), key: z.string().min(1), name: z.string().min(1), description: z.string().optional(), matchType: z.enum(['ALL_MATCH_TYPE', 'ANY_MATCH_TYPE']), }, async args => { try { const currentSegment = await fliptClient.getSegment(args.namespaceKey, args.key); if (!currentSegment) { return { content: [ { type: 'text', text: `Segment ${args.key} in namespace ${args.namespaceKey} not found`, }, ], isError: true, }; } // update changed fields const updatedSegment = { ...currentSegment, name: args.name || currentSegment.name, description: args.description || currentSegment.description, matchType: args.matchType || currentSegment.matchType, }; const segment = await fliptClient.updateSegment( currentSegment.namespaceKey!, currentSegment.key!, updatedSegment.name!, updatedSegment.description, updatedSegment.matchType ); return { content: [ { type: 'text', text: JSON.stringify(segment, null, 2), }, ], _meta: { uri: `flipt://namespaces/${args.namespaceKey}/segments/${args.key}`, }, }; } catch (error: any) { console.error('Error updating segment:', error); return { content: [ { type: 'text', text: `Failed to update segment: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'delete_segment', { namespaceKey: z.string().min(1), key: z.string().min(1), }, async args => { try { await fliptClient.deleteSegment(args.namespaceKey, args.key); return { content: [ { type: 'text', text: `Deleted segment ${args.key} in namespace ${args.namespaceKey}`, }, ], }; } catch (error: any) { console.error('Error deleting segment:', error); return { content: [ { type: 'text', text: `Failed to delete segment: ${error.message}`, }, ], isError: true, }; } } ); // Evaluation tools server.tool( 'evaluate_boolean_flag', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), entityId: z.string().min(1), context: z.record(z.string()).optional(), }, async args => { try { const response = await fliptClient.evaluateBoolean( args.namespaceKey, args.flagKey, args.entityId, args.context ); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error: any) { console.error('Error evaluating boolean flag:', error); return { content: [ { type: 'text', text: `Failed to evaluate boolean flag: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'evaluate_variant_flag', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), entityId: z.string().min(1), context: z.record(z.string()).optional(), }, async args => { try { const response = await fliptClient.evaluateVariant( args.namespaceKey, args.flagKey, args.entityId, args.context ); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error: any) { console.error('Error evaluating variant flag:', error); return { content: [ { type: 'text', text: `Failed to evaluate variant flag: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'evaluate_batch', { requests: z.array( z.object({ namespaceKey: z.string().min(1), flagKey: z.string().min(1), entityId: z.string().min(1), context: z.record(z.string()).optional(), }) ), }, async args => { try { const response = await fliptClient.evaluateBatch(args.requests); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error: any) { console.error('Error evaluating batch:', error); return { content: [ { type: 'text', text: `Failed to evaluate batch: ${error.message}`, }, ], isError: true, }; } } ); // Constraint tools server.tool( 'create_constraint', { namespaceKey: z.string().min(1), segmentKey: z.string().min(1), type: z.enum([ 'STRING_COMPARISON_TYPE', 'NUMBER_COMPARISON_TYPE', 'BOOLEAN_COMPARISON_TYPE', 'DATETIME_COMPARISON_TYPE', 'ENTITY_ID_COMPARISON_TYPE', ]), property: z.string().min(1), operator: z.string().min(1), value: z.string().optional(), description: z.string().optional(), }, async args => { try { const response = await fliptClient.createConstraint( args.namespaceKey, args.segmentKey, args.type, args.property, args.operator, args.value, args.description ); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error: any) { console.error('Error creating constraint:', error); return { content: [ { type: 'text', text: `Failed to create constraint: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'delete_constraint', { namespaceKey: z.string().min(1), segmentKey: z.string().min(1), constraintId: z.string().min(1), }, async args => { try { await fliptClient.deleteConstraint(args.namespaceKey, args.segmentKey, args.constraintId); return { content: [ { type: 'text', text: `Successfully deleted constraint ${args.constraintId} from segment ${args.segmentKey}`, }, ], }; } catch (error: any) { console.error('Error deleting constraint:', error); return { content: [ { type: 'text', text: `Failed to delete constraint: ${error.message}`, }, ], isError: true, }; } } ); // Variant tools server.tool( 'create_variant', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), key: z.string().min(1), name: z.string().optional(), description: z.string().optional(), attachment: z.string().optional(), }, async args => { try { const response = await fliptClient.createVariant( args.namespaceKey, args.flagKey, args.key, args.name, args.description, args.attachment ); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error: any) { console.error('Error creating variant:', error); return { content: [ { type: 'text', text: `Failed to create variant: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'delete_variant', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), variantId: z.string().min(1), }, async args => { try { await fliptClient.deleteVariant(args.namespaceKey, args.flagKey, args.variantId); return { content: [ { type: 'text', text: `Successfully deleted variant ${args.variantId} from flag ${args.flagKey}`, }, ], }; } catch (error: any) { console.error('Error deleting variant:', error); return { content: [ { type: 'text', text: `Failed to delete variant: ${error.message}`, }, ], isError: true, }; } } ); // Rule tools server.tool( 'create_rule', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), segmentKey: z.string().min(1), rank: z.number().int().optional(), segmentOperator: z.enum(['OR_SEGMENT_OPERATOR', 'AND_SEGMENT_OPERATOR']).optional(), }, async args => { try { const response = await fliptClient.createRule( args.namespaceKey, args.flagKey, args.segmentKey, args.rank, args.segmentOperator ); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error: any) { console.error('Error creating rule:', error); return { content: [ { type: 'text', text: `Failed to create rule: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'delete_rule', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), ruleId: z.string().min(1), }, async args => { try { await fliptClient.deleteRule(args.namespaceKey, args.flagKey, args.ruleId); return { content: [ { type: 'text', text: `Successfully deleted rule ${args.ruleId} from flag ${args.flagKey}`, }, ], }; } catch (error: any) { console.error('Error deleting rule:', error); return { content: [ { type: 'text', text: `Failed to delete rule: ${error.message}`, }, ], isError: true, }; } } ); // Distribution tools server.tool( 'create_distribution', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), ruleId: z.string().min(1), variantId: z.string().min(1), rollout: z.number().min(0).max(100), }, async args => { try { const response = await fliptClient.createDistribution( args.namespaceKey, args.flagKey, args.ruleId, args.variantId, args.rollout ); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error: any) { console.error('Error creating distribution:', error); return { content: [ { type: 'text', text: `Failed to create distribution: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'delete_distribution', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), ruleId: z.string().min(1), distributionId: z.string().min(1), }, async args => { try { await fliptClient.deleteDistribution( args.namespaceKey, args.flagKey, args.ruleId, args.distributionId ); return { content: [ { type: 'text', text: `Successfully deleted distribution ${args.distributionId} from rule ${args.ruleId}`, }, ], }; } catch (error: any) { console.error('Error deleting distribution:', error); return { content: [ { type: 'text', text: `Failed to delete distribution: ${error.message}`, }, ], isError: true, }; } } ); // Rollout tools server.tool( 'create_rollout', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), rank: z.number().int().min(1), description: z.string().optional(), segment: z .object({ segmentKey: z.string().min(1), value: z.boolean().optional(), }) .optional(), threshold: z .object({ percentage: z.number().min(0).max(100), value: z.boolean(), }) .optional(), }, async args => { try { const response = await fliptClient.createRollout( args.namespaceKey, args.flagKey, args.rank, args.description, args.segment, args.threshold ); return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], }; } catch (error: any) { console.error('Error creating rollout:', error); return { content: [ { type: 'text', text: `Failed to create rollout: ${error.message}`, }, ], isError: true, }; } } ); server.tool( 'delete_rollout', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), rolloutId: z.string().min(1), }, async args => { try { await fliptClient.deleteRollout(args.namespaceKey, args.flagKey, args.rolloutId); return { content: [ { type: 'text', text: `Successfully deleted rollout ${args.rolloutId} from flag ${args.flagKey}`, }, ], }; } catch (error: any) { console.error('Error deleting rollout:', error); return { content: [ { type: 'text', text: `Failed to delete rollout: ${error.message}`, }, ], isError: true, }; } } ); // Now let's add some prompts for common tasks server.prompt( 'create_boolean_flag', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), flagName: z.string().min(1), description: z.string().optional(), }, args => ({ messages: [ { role: 'user', content: { type: 'text', text: `Create a new boolean flag named "${args.flagName}" with key "${args.flagKey}" in namespace "${args.namespaceKey}"${args.description ? ` with description "${args.description}"` : ''}.`, }, }, ], }) ); server.prompt( 'create_variant_flag', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), flagName: z.string().min(1), description: z.string().optional(), variantKeys: z.string().optional(), }, args => ({ messages: [ { role: 'user', content: { type: 'text', text: `Create a new variant flag named "${args.flagName}" with key "${args.flagKey}" in namespace "${args.namespaceKey}"${args.description ? ` with description "${args.description}"` : ''}${args.variantKeys ? ` with variants: ${args.variantKeys}` : ''}.`, }, }, ], }) ); server.prompt( 'create_segment', { namespaceKey: z.string().min(1), segmentKey: z.string().min(1), segmentName: z.string().min(1), description: z.string().optional(), constraintDescription: z.string().optional(), }, args => ({ messages: [ { role: 'user', content: { type: 'text', text: `Create a new segment named "${args.segmentName}" with key "${args.segmentKey}" in namespace "${args.namespaceKey}"${args.description ? ` with description "${args.description}"` : ''}${args.constraintDescription ? ` with constraints: ${args.constraintDescription}` : ''}.`, }, }, ], }) ); server.prompt( 'evaluate_flag', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), entityId: z.string().min(1), contextDescription: z.string().optional(), }, args => ({ messages: [ { role: 'user', content: { type: 'text', text: `Evaluate flag "${args.flagKey}" in namespace "${args.namespaceKey}" for entity "${args.entityId}"${args.contextDescription ? ` with context: ${args.contextDescription}` : ''}.`, }, }, ], }) ); server.prompt( 'toggle_flag', { namespaceKey: z.string().min(1), flagKey: z.string().min(1), enabled: z.enum(['true', 'false']), }, args => ({ messages: [ { role: 'user', content: { type: 'text', text: `${args.enabled === 'true' ? 'Enable' : 'Disable'} flag "${args.flagKey}" in namespace "${args.namespaceKey}".`, }, }, ], }) ); server.prompt( 'list_enabled_flags', { namespaceKey: z.string().min(1), }, args => ({ messages: [ { role: 'user', content: { type: 'text', text: `List all enabled flags in namespace "${args.namespaceKey}".`, }, }, ], }) ); server.prompt( 'list_disabled_flags', { namespaceKey: z.string().min(1), }, args => ({ messages: [ { role: 'user', content: { type: 'text', text: `List all disabled flags in namespace "${args.namespaceKey}".`, }, }, ], }) ); // Function to start the server function startServer() { // Connect the server to STDIO transport const transport = new StdioServerTransport(); server.connect(transport); console.log(`Flipt MCP Server running`); } // If this file is run directly, start the server if (require.main === module) { startServer(); } // Export for use in other modules export { server, startServer };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/flipt-io/mcp-server-flipt'

If you have feedback or need assistance with the MCP directory API, please join our Discord server