mcp-orchestrator
Version:
MCP Orchestrator - Discover and install MCPs with automatic OAuth support. Uses Claude CLI for OAuth MCPs (Canva, Asana, etc). 34 trusted MCPs from Claude Partners.
450 lines (449 loc) ⢠19.3 kB
JavaScript
/**
* MCP Orchestrator Server
* Main entry point - creates the orchestrator MCP server
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import { ConfigManager } from './config-manager.js';
import { MCP_REGISTRY, findMCPById, searchMCPs } from './mcp-registry.js';
import { discoverMCPs } from './discovery.js';
async function main() {
console.error('š Starting MCP Orchestrator Server...\n');
// Create config manager
const configManager = new ConfigManager();
// Create orchestrator server
const server = new McpServer({
name: 'mcp-orchestrator',
version: '0.5.0'
}, {
capabilities: {}
});
// ============================================
// META-TOOL: list_available_mcps
// ============================================
server.registerTool('list_available_mcps', {
title: 'List Available MCP Servers',
description: 'Browse all MCP servers available in the registry',
inputSchema: {
search: z.string().optional().describe('Optional search query to filter MCPs')
},
outputSchema: {
mcps: z.array(z.object({
id: z.string(),
name: z.string(),
description: z.string()
})),
count: z.number()
}
}, async ({ search }) => {
console.error(`š Listing available MCPs (search: ${search || 'none'})...`);
const mcps = search ? searchMCPs(search) : MCP_REGISTRY;
const output = {
mcps: mcps.map(mcp => ({
id: mcp.id,
name: mcp.name,
description: mcp.description
})),
count: mcps.length
};
return {
content: [
{
type: 'text',
text: `Found ${output.count} MCP servers:\n\n${mcps.map(m => `⢠${m.name} (${m.id}): ${m.description}`).join('\n')}`
}
],
structuredContent: output
};
});
// ============================================
// META-TOOL: discover_mcps (INTELLIGENT DISCOVERY)
// ============================================
server.registerTool('discover_mcps', {
title: 'Discover Relevant MCP Servers',
description: 'Intelligently discover which MCP servers would be most helpful for your specific task',
inputSchema: {
taskDescription: z.string().describe('Describe what you want to accomplish (e.g., "I want to read JSON files from my project")')
},
outputSchema: {
recommendations: z.array(z.object({
id: z.string(),
name: z.string(),
description: z.string(),
score: z.number(),
reasons: z.array(z.string()),
matchedKeywords: z.array(z.string()),
installTime: z.string().optional(),
pros: z.array(z.string()).optional(),
cons: z.array(z.string()).optional(),
useCases: z.array(z.string()).optional()
})),
analyzedContext: z.object({
keywords: z.array(z.string()),
categories: z.array(z.string()),
taskType: z.string()
})
}
}, async ({ taskDescription }) => {
console.error(`š Discovering MCPs for: "${taskDescription}"...`);
const results = discoverMCPs(taskDescription, 5);
// Get the context for transparency
const { analyzeTaskContext } = await import('./discovery.js');
const context = analyzeTaskContext(taskDescription);
const output = {
recommendations: results.map(r => ({
id: r.mcp.id,
name: r.mcp.name,
description: r.mcp.description,
score: r.score,
reasons: r.reasons,
matchedKeywords: r.matchedKeywords,
installTime: r.mcp.installTime,
pros: r.mcp.pros,
cons: r.mcp.cons,
useCases: r.mcp.useCases
})),
analyzedContext: {
keywords: context.keywords,
categories: context.categories,
taskType: context.taskType
}
};
const responseText = results.length > 0
? `šÆ Found ${results.length} relevant MCP servers:\n\n${results.map((r, i) => {
let text = `${i + 1}. **${r.mcp.name}** (score: ${r.score})\n ${r.mcp.description}`;
if (r.mcp.installTime) {
text += `\n ā±ļø Install time: ${r.mcp.installTime}`;
}
text += `\n ā ${r.reasons.join('\n ā ')}`;
if (r.mcp.pros && r.mcp.pros.length > 0) {
text += `\n š Pros: ${r.mcp.pros.slice(0, 2).join(', ')}`;
}
if (r.mcp.cons && r.mcp.cons.length > 0) {
text += `\n ā ļø Cons: ${r.mcp.cons.slice(0, 2).join(', ')}`;
}
return text;
}).join('\n\n')}\n\nš Analysis:\n⢠Keywords: ${context.keywords.slice(0, 5).join(', ')}\n⢠Categories: ${context.categories.join(', ') || 'none'}\n⢠Task type: ${context.taskType}`
: `No relevant MCPs found for: "${taskDescription}"`;
return {
content: [
{
type: 'text',
text: responseText
}
],
structuredContent: output
};
});
// ============================================
// META-TOOL: connect_mcp (adds to config)
// ============================================
server.registerTool('connect_mcp', {
title: 'Connect to MCP Server',
description: 'Connect to a specific MCP server and load its tools',
inputSchema: {
mcpId: z.string().describe('ID of the MCP server to connect (e.g., "filesystem", "github")'),
autoInstall: z.boolean().optional().describe('Automatically install the MCP if not present (default: true)')
},
outputSchema: {
success: z.boolean(),
serverId: z.string(),
configPath: z.string().optional(),
product: z.string().optional(),
message: z.string(),
requiresRestart: z.boolean(),
error: z.string().optional()
}
}, async ({ mcpId, autoInstall = true }) => {
try {
console.error(`š Adding MCP to config: ${mcpId}...`);
// Find MCP config
const config = findMCPById(mcpId);
if (!config) {
const output = {
success: false,
serverId: mcpId,
message: `MCP server "${mcpId}" not found in registry`,
requiresRestart: false,
error: `MCP server "${mcpId}" not found in registry`
};
return {
content: [{ type: 'text', text: `ā ${output.error}` }],
structuredContent: output,
isError: true
};
}
// Check if this MCP requires OAuth
const needsOAuth = configManager.requiresOAuth(config);
if (needsOAuth) {
// Use Claude CLI for OAuth MCPs (handles OAuth flow automatically)
console.error(`š ${config.name} requires OAuth - using Claude CLI...`);
// Auto-install package first if needed
let wasInstalled = false;
if (autoInstall && config.packageName) {
const { autoInstall: runAutoInstall } = await import('./auto-installer.js');
const installResult = await runAutoInstall(config);
if (!installResult.success && !installResult.alreadyInstalled) {
const output = {
success: false,
serverId: mcpId,
message: `Failed to install ${config.name}: ${installResult.error}`,
requiresRestart: false,
error: `Failed to install ${config.name}: ${installResult.error}`
};
return {
content: [{ type: 'text', text: `ā ${output.error}` }],
structuredContent: output,
isError: true
};
}
wasInstalled = !installResult.alreadyInstalled;
}
// Run claude mcp add (will open browser for OAuth)
const cliResult = configManager.addMCPViaCLI(config);
if (!cliResult.success) {
return {
content: [{ type: 'text', text: `ā ${cliResult.message}` }],
structuredContent: {
success: false,
serverId: mcpId,
message: cliResult.message,
requiresRestart: false,
error: cliResult.message
},
isError: true
};
}
const installMsg = wasInstalled ? 'ā
Installed and added' : 'ā
Added';
return {
content: [
{
type: 'text',
text: `${installMsg} ${config.name} via Claude CLI!\n\nš OAuth authorization completed.\n\nā ļø You must RESTART Claude Code for changes to take effect.`
}
],
structuredContent: {
success: true,
serverId: mcpId,
message: cliResult.message,
requiresRestart: true,
usedCLI: true
}
};
}
// Non-OAuth MCP - use config file approach
let wasInstalled = false;
if (autoInstall && config.packageName) {
const { autoInstall: runAutoInstall } = await import('./auto-installer.js');
const installResult = await runAutoInstall(config);
if (!installResult.success && !installResult.alreadyInstalled) {
const output = {
success: false,
serverId: mcpId,
message: `Failed to install ${config.name}: ${installResult.error}`,
requiresRestart: false,
error: `Failed to install ${config.name}: ${installResult.error}`
};
return {
content: [{ type: 'text', text: `ā ${output.error}` }],
structuredContent: output,
isError: true
};
}
wasInstalled = !installResult.alreadyInstalled;
}
// Add to config
const result = configManager.addMCPToConfig(config);
if (!result.success) {
return {
content: [{ type: 'text', text: `ā ${result.message}` }],
structuredContent: {
success: false,
serverId: mcpId,
configPath: result.configPath,
product: result.product,
message: result.message || 'Failed to add MCP to config',
requiresRestart: false,
error: result.message
},
isError: true
};
}
const output = {
success: true,
serverId: mcpId,
configPath: result.configPath,
product: result.product,
message: result.message,
requiresRestart: true
};
const installMessage = wasInstalled ? `ā
Installed and ` : 'ā
';
const restartMessage = result.product === 'code'
? 'ā ļø You must RESTART Claude Code for changes to take effect.'
: 'ā ļø You must RESTART Claude Desktop for changes to take effect.';
return {
content: [
{
type: 'text',
text: `${installMessage}${result.message}\n\n${restartMessage}\n\nConfig file: ${result.configPath}`
}
],
structuredContent: output
};
}
catch (error) {
console.error(`ā Unexpected error in connect_mcp:`, error);
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [{ type: 'text', text: `ā Unexpected error: ${errorMessage}` }],
structuredContent: {
success: false,
serverId: mcpId,
message: errorMessage,
requiresRestart: false,
error: errorMessage
},
isError: true
};
}
});
// ============================================
// META-TOOL: list_active_mcps (from config)
// ============================================
server.registerTool('list_active_mcps', {
title: 'List MCP Servers in Config',
description: 'Show all currently connected MCP servers',
inputSchema: {},
outputSchema: {
success: z.boolean(),
mcps: z.array(z.string()),
count: z.number(),
configPath: z.string(),
product: z.string()
}
}, async () => {
console.error('š Listing MCPs in config...');
const result = configManager.listMCPsInConfig();
const output = {
success: result.success,
mcps: result.mcps,
count: result.mcps.length,
configPath: result.configPath,
product: result.product
};
return {
content: [
{
type: 'text',
text: result.mcps.length > 0
? `MCPs in ${result.product === 'code' ? 'Claude Code' : 'Claude Desktop'} config (${result.mcps.length}):\n\n${result.mcps.map(id => `⢠${id}`).join('\n')}\n\nConfig file: ${result.configPath}`
: `No MCPs in ${result.product === 'code' ? 'Claude Code' : 'Claude Desktop'} config\n\nConfig file: ${result.configPath}`
}
],
structuredContent: output
};
});
// ============================================
// META-TOOL: disconnect_mcp (remove from config)
// ============================================
server.registerTool('disconnect_mcp', {
title: 'Remove MCP from Config',
description: 'Disconnect from an active MCP server',
inputSchema: {
connectionId: z.string().describe('ID of the connection to close')
},
outputSchema: {
success: z.boolean(),
message: z.string(),
requiresRestart: z.boolean(),
error: z.string().optional()
}
}, async ({ connectionId }) => {
console.error(`š Removing MCP from config: ${connectionId}...`);
const result = configManager.removeMCPFromConfig(connectionId);
const output = {
success: result.success,
message: result.message,
requiresRestart: result.success,
error: result.success ? undefined : result.message
};
const restartMessage = result.success
? `\n\nā ļø You must RESTART ${result.product === 'code' ? 'Claude Code' : 'Claude Desktop'} for changes to take effect.`
: '';
return {
content: [
{
type: 'text',
text: result.success ? `ā
${result.message}${restartMessage}` : `ā ${result.message}`
}
],
structuredContent: output,
isError: !result.success
};
});
// ============================================
// META-TOOL: generate_add_command
// ============================================
server.registerTool('generate_add_command', {
title: 'Generate Claude MCP Add Command',
description: 'Generate a `claude mcp add` command for manual installation',
inputSchema: {
mcpId: z.string().describe('ID of the MCP server')
},
outputSchema: {
success: z.boolean(),
command: z.string().optional(),
error: z.string().optional()
}
}, async ({ mcpId }) => {
console.error(`š Generating add command for: ${mcpId}...`);
const config = findMCPById(mcpId);
if (!config) {
return {
content: [{ type: 'text', text: `ā MCP server "${mcpId}" not found in registry` }],
structuredContent: {
success: false,
error: `MCP server "${mcpId}" not found in registry`
},
isError: true
};
}
const command = configManager.generateAddCommand(config);
return {
content: [
{
type: 'text',
text: `To add ${config.name}, run this command:\n\n\`\`\`\n${command}\n\`\`\``
}
],
structuredContent: {
success: true,
command
}
};
});
// Connect to stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('ā
MCP Orchestrator is ready!\n');
console.error('Available meta-tools:');
console.error(' ⢠discover_mcps - šÆ INTELLIGENT: Discover relevant MCPs for your task');
console.error(' ⢠list_available_mcps - Browse available MCP servers');
console.error(' ⢠connect_mcp - Add MCP to Claude config (requires restart)');
console.error(' ⢠list_active_mcps - Show MCPs in config');
console.error(' ⢠disconnect_mcp - Remove MCP from config (requires restart)');
console.error(' ⢠generate_add_command - Generate `claude mcp add` command\n');
}
// Handle CLI commands
if (process.argv.includes('setup-claude')) {
// Run setup instead of starting server
import('./setup-claude.js').then(module => {
// Setup will run automatically
}).catch(console.error);
}
else {
// Normal server mode
main().catch(console.error);
}