@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.
95 lines (94 loc) • 3.75 kB
JavaScript
/**
* List Bundles Tool
* Search and paginate through bundles with unified scope model
*/
import { ApiClient } from '../../utils/api-client.js';
import { formatEmptyResults, formatPaginationInfo, formatNextSteps, formatDate, formatTags } from '../shared/formatters.js';
export const listBundlesTool = {
name: 'list_bundles',
description: 'Find bundles by searching name, description, and tags. Bundles are collections of MCP servers packaged together for easy installation. Returns your organization\'s bundles only. Supports pagination and full-text search.',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Optional search term to find bundles by name, description, or tags. Examples: "web development", "github", "data analysis", "browser"'
},
limit: {
type: 'number',
minimum: 1,
maximum: 100,
default: 20,
description: 'Maximum number of bundles to return (1-100, default: 20)'
},
offset: {
type: 'number',
minimum: 0,
default: 0,
description: 'Number of bundles to skip for pagination (default: 0)'
}
}
},
async execute(args, context) {
try {
// Validate user context
if (!context.userId) {
throw new Error('User authentication required');
}
// Set defaults
const query = args.query || '';
const limit = Math.min(args.limit || 20, 100);
const offset = args.offset || 0;
// Use API client to fetch bundles (organization only)
// Your Vercel API handles the Supabase query with service role key
const bundles = await ApiClient.searchBundles({
query,
limit,
offset
}, context);
// Handle empty results
const bundlesArray = bundles;
if (!bundlesArray || bundlesArray.length === 0) {
return formatEmptyResults({ query }, 'bundles');
}
// Format results
const searchText = query ? ` matching "${query}"` : '';
const resultsText = `🔍 Found ${bundlesArray.length} organization bundle${bundlesArray.length === 1 ? '' : 's'}${searchText}:
`;
const bundlesList = bundlesArray.map((bundle, index) => {
const tagsText = formatTags(bundle.tags);
const createdDate = formatDate(bundle.created_at);
return `**${index + 1}. ${bundle.name}**
📝 ${bundle.description}
🆔 Bundle ID: ${bundle.id}
📅 Created: ${createdDate}${tagsText}`;
}).join('\n\n');
const paginationInfo = formatPaginationInfo({
limit,
offset,
totalResults: bundlesArray.length
});
const nextStepsText = formatNextSteps('bundles');
return {
content: [
{
type: 'text',
text: resultsText + bundlesList + paginationInfo + nextStepsText
}
]
};
}
catch (error) {
console.error('List bundles error:', error);
return {
content: [
{
type: 'text',
text: `❌ Failed to list bundles: ${error.message}`
}
]
};
}
}
};
//# sourceMappingURL=list-bundles.js.map