@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
JavaScript
/**
* 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