UNPKG

@grebyn/toolflow-mcp-server

Version:

MCP server for managing other MCP servers - discover, install, organize into bundles, and automate with workflows. Uses StreamableHTTP transport with dual OAuth/API key authentication.

125 lines (122 loc) 6.41 kB
/** * Create Bundle Tool * Save a collection of MCP servers as a reusable bundle */ import { ApiClient } from '../../utils/api-client.js'; export const createBundleTool = { name: 'create_bundle', description: 'Save a collection of MCP servers as a reusable bundle. Bundles allow you to package multiple MCP servers together for easy installation. **IMPORTANT: Use clear, descriptive names** like "NextJS Deployment", "Email Toolkit", "Database Management Suite", "AI Development Tools", "Web Scraping Bundle" - NOT generic names like "my-bundle" or "test-1". All bundles are created as private to your organization for security. Examples: "Web Development Bundle" with GitHub, filesystem, and browser tools; "Data Analysis Bundle" with database, Excel, and visualization tools.', inputSchema: { type: 'object', properties: { name: { type: 'string', description: 'Bundle name (2-100 characters). Should be descriptive and unique within your organization. **Use clear, action-oriented names** like: "NextJS Deployment", "Email Toolkit", "Database Management Suite", "AI Development Tools", "Web Scraping Bundle", "API Testing Suite", "DevOps Automation". Avoid generic names like "my-bundle" or "test-1".', minLength: 2, maxLength: 100 }, description: { type: 'string', description: 'Bundle description (5-500 characters). Explain what the bundle does and what MCP servers it contains. This helps others discover and understand your bundle.', minLength: 5, maxLength: 500 }, servers: { type: 'object', description: 'MCP server configurations in the bundle. Each key is the server name, value is the config with command, args, env, and optional type', additionalProperties: { type: 'object', properties: { command: { type: 'string' }, args: { type: 'array', items: { type: 'string' } }, env: { type: 'object', additionalProperties: { type: 'string' } }, type: { type: 'string', enum: ['stdio', 'http', 'sse'] } }, required: ['command', 'args'] } }, tags: { type: 'array', description: 'Optional tags for categorization. Examples: ["web-development", "github", "filesystem"], ["data-analysis", "database", "excel"]', items: { type: 'string' }, default: [] }, install_commands: { type: 'array', description: 'Optional array of shell commands to run before installing the MCP servers. Examples: ["npm install -g @modelcontextprotocol/server-filesystem", "pip install mcp-server-git"]', items: { type: 'string' }, default: [] }, }, required: ['name', 'description', 'servers'] }, async execute(args, context) { try { // Validate user has authentication token if (!context.token) { throw new Error('User authentication required'); } // Validate servers format if (!args.servers || Object.keys(args.servers).length === 0) { throw new Error('Bundle must contain at least one MCP server configuration'); } // Validate server configurations (no sanitization - user controls what they share) for (const [serverName, serverConfig] of Object.entries(args.servers)) { if (!serverConfig.command || typeof serverConfig.command !== 'string') { throw new Error(`Server '${serverName}' must have a valid command`); } if (!Array.isArray(serverConfig.args)) { throw new Error(`Server '${serverName}' must have valid args array`); } if (serverConfig.env && typeof serverConfig.env !== 'object') { throw new Error(`Server '${serverName}' env must be an object`); } if (serverConfig.type && !['stdio', 'http', 'sse'].includes(serverConfig.type)) { throw new Error(`Server '${serverName}' type must be 'stdio', 'http', or 'sse'`); } } // Call API to create bundle - API handles organization lookup and validation const bundleData = await ApiClient.createBundle({ name: args.name.trim(), description: args.description.trim(), servers: args.servers, tags: args.tags || [], install_commands: args.install_commands || [] }, context); const serverCount = Object.keys(args.servers).length; return { content: [ { type: 'text', text: `✅ Bundle created successfully! **Bundle Details:** - **Name**: ${bundleData.name} - **Description**: ${bundleData.description} - **Servers**: ${serverCount} MCP server${serverCount === 1 ? '' : 's'} - **Bundle ID**: ${bundleData.id} - **Created**: ${new Date(bundleData.created_at).toLocaleString()} ${args.tags && args.tags.length > 0 ? `**Tags**: ${args.tags.join(', ')}\n` : ''} **Server List**: ${Object.keys(args.servers).join(', ')} ${args.install_commands && args.install_commands.length > 0 ? `\n**Installation Commands**:\n${args.install_commands.map(cmd => `- \`${cmd}\``).join('\n')}` : ''} **Next Steps:** - Use \`list_bundles\` to find this bundle later - Use \`get_bundle_config\` to retrieve the configuration for installation - Share the bundle name with others if it's public` } ] }; } catch (error) { console.error('Create bundle error:', error); return { content: [ { type: 'text', text: `❌ Failed to create bundle: ${error.message}` } ] }; } } }; //# sourceMappingURL=create-bundle.js.map