🎉 Complete CCLI Integration: Phase 4 (MCP Server Updates)
IMPLEMENTATION COMPLETE: Successfully integrated Google Gemini CLI as mixed agent type in Hive distributed AI platform. ## Phase 4 Achievements: ✅ Enhanced MCP tools with CLI agent support ✅ Added hive_register_cli_agent, hive_get_cli_agents tools ✅ Updated HiveClient interface for CLI agent management ✅ Mixed agent type coordination via MCP ✅ Comprehensive error handling and user feedback ## Key Features: - CLI agent registration with health checks - Mixed agent dashboard (🤖 Ollama + ⚡ CLI) - Predefined agent quick setup (walnut-gemini, ironwood-gemini) - SSH-based task execution with connection pooling - Complete backward compatibility ## Technical Stack: - MCP Tools: CLI agent management interface - HiveClient: Enhanced API client with CLI support - TypeScript: Full type safety for mixed agent operations - Error Handling: Comprehensive CLI connectivity validation ## Production Ready: ✅ 16 MCP tools with CLI agent coverage ✅ Mixed agent type task coordination ✅ Health monitoring and statistics collection ✅ Robust SSH execution with timeout handling ✅ Integration tested and validated Ready for hybrid AI orchestration: 5 Ollama + 2 CLI agents 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,17 @@ export interface Agent {
|
||||
status: 'available' | 'busy' | 'offline';
|
||||
current_tasks: number;
|
||||
max_concurrent: number;
|
||||
agent_type?: 'ollama' | 'cli';
|
||||
cli_config?: {
|
||||
host?: string;
|
||||
node_version?: string;
|
||||
model?: string;
|
||||
specialization?: string;
|
||||
max_concurrent?: number;
|
||||
command_timeout?: number;
|
||||
ssh_timeout?: number;
|
||||
agent_type?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Task {
|
||||
@@ -97,6 +108,47 @@ export class HiveClient {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// CLI Agent Management
|
||||
async getCliAgents(): Promise<Agent[]> {
|
||||
const response = await this.api.get('/api/cli-agents/');
|
||||
return response.data || [];
|
||||
}
|
||||
|
||||
async registerCliAgent(agentData: {
|
||||
id: string;
|
||||
host: string;
|
||||
node_version: string;
|
||||
model?: string;
|
||||
specialization?: string;
|
||||
max_concurrent?: number;
|
||||
agent_type?: string;
|
||||
command_timeout?: number;
|
||||
ssh_timeout?: number;
|
||||
}): Promise<{ agent_id: string; endpoint: string; health_check?: any }> {
|
||||
const response = await this.api.post('/api/cli-agents/register', agentData);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async registerPredefinedCliAgents(): Promise<{ results: any[] }> {
|
||||
const response = await this.api.post('/api/cli-agents/register-predefined');
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async healthCheckCliAgent(agentId: string): Promise<any> {
|
||||
const response = await this.api.post(`/api/cli-agents/${agentId}/health-check`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async getCliAgentStatistics(): Promise<any> {
|
||||
const response = await this.api.get('/api/cli-agents/statistics/all');
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async unregisterCliAgent(agentId: string): Promise<{ success: boolean }> {
|
||||
const response = await this.api.delete(`/api/cli-agents/${agentId}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// Task Management
|
||||
async createTask(taskData: {
|
||||
type: string;
|
||||
|
||||
@@ -40,7 +40,7 @@ export class HiveTools {
|
||||
model: { type: 'string', description: 'Model name (e.g., codellama:34b)' },
|
||||
specialty: {
|
||||
type: 'string',
|
||||
enum: ['kernel_dev', 'pytorch_dev', 'profiler', 'docs_writer', 'tester'],
|
||||
enum: ['kernel_dev', 'pytorch_dev', 'profiler', 'docs_writer', 'tester', 'cli_gemini', 'general_ai', 'reasoning'],
|
||||
description: 'Agent specialization area'
|
||||
},
|
||||
max_concurrent: { type: 'number', description: 'Maximum concurrent tasks', default: 2 },
|
||||
@@ -48,6 +48,46 @@ export class HiveTools {
|
||||
required: ['id', 'endpoint', 'model', 'specialty'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'hive_register_cli_agent',
|
||||
description: 'Register a new CLI-based AI agent (e.g., Gemini CLI) in the Hive cluster',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique CLI agent identifier' },
|
||||
host: { type: 'string', description: 'SSH hostname (e.g., walnut, ironwood)' },
|
||||
node_version: { type: 'string', description: 'Node.js version (e.g., v22.14.0)' },
|
||||
model: { type: 'string', description: 'Model name (e.g., gemini-2.5-pro)', default: 'gemini-2.5-pro' },
|
||||
specialization: {
|
||||
type: 'string',
|
||||
enum: ['general_ai', 'reasoning', 'code_analysis', 'documentation', 'testing'],
|
||||
description: 'CLI agent specialization',
|
||||
default: 'general_ai'
|
||||
},
|
||||
max_concurrent: { type: 'number', description: 'Maximum concurrent tasks', default: 2 },
|
||||
agent_type: { type: 'string', description: 'CLI agent type', default: 'gemini' },
|
||||
command_timeout: { type: 'number', description: 'Command timeout in seconds', default: 60 },
|
||||
ssh_timeout: { type: 'number', description: 'SSH timeout in seconds', default: 5 },
|
||||
},
|
||||
required: ['id', 'host', 'node_version'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'hive_get_cli_agents',
|
||||
description: 'Get all registered CLI agents in the Hive cluster',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'hive_register_predefined_cli_agents',
|
||||
description: 'Register predefined CLI agents (walnut-gemini, ironwood-gemini) with verified configurations',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
|
||||
// Task Management Tools
|
||||
{
|
||||
@@ -58,7 +98,7 @@ export class HiveTools {
|
||||
properties: {
|
||||
type: {
|
||||
type: 'string',
|
||||
enum: ['kernel_dev', 'pytorch_dev', 'profiler', 'docs_writer', 'tester'],
|
||||
enum: ['kernel_dev', 'pytorch_dev', 'profiler', 'docs_writer', 'tester', 'cli_gemini', 'general_ai', 'reasoning'],
|
||||
description: 'Type of development task'
|
||||
},
|
||||
priority: {
|
||||
@@ -204,7 +244,7 @@ export class HiveTools {
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
specialization: { type: 'string', enum: ['kernel_dev', 'pytorch_dev', 'profiler', 'docs_writer', 'tester'] },
|
||||
specialization: { type: 'string', enum: ['kernel_dev', 'pytorch_dev', 'profiler', 'docs_writer', 'tester', 'cli_gemini', 'general_ai', 'reasoning'] },
|
||||
task_description: { type: 'string' },
|
||||
dependencies: { type: 'array', items: { type: 'string' } },
|
||||
priority: { type: 'number', minimum: 1, maximum: 5 }
|
||||
@@ -254,6 +294,15 @@ export class HiveTools {
|
||||
|
||||
case 'hive_register_agent':
|
||||
return await this.registerAgent(args);
|
||||
|
||||
case 'hive_register_cli_agent':
|
||||
return await this.registerCliAgent(args);
|
||||
|
||||
case 'hive_get_cli_agents':
|
||||
return await this.getCliAgents();
|
||||
|
||||
case 'hive_register_predefined_cli_agents':
|
||||
return await this.registerPredefinedCliAgents();
|
||||
|
||||
// Task Management
|
||||
case 'hive_create_task':
|
||||
@@ -313,20 +362,48 @@ export class HiveTools {
|
||||
|
||||
private async getAgents() {
|
||||
const agents = await this.hiveClient.getAgents();
|
||||
|
||||
// Group agents by type
|
||||
const ollamaAgents = agents.filter(agent => !agent.agent_type || agent.agent_type === 'ollama');
|
||||
const cliAgents = agents.filter(agent => agent.agent_type === 'cli');
|
||||
|
||||
const formatAgent = (agent: any) => {
|
||||
const typeIcon = agent.agent_type === 'cli' ? '⚡' : '🤖';
|
||||
const typeLabel = agent.agent_type === 'cli' ? 'CLI' : 'API';
|
||||
|
||||
return `${typeIcon} **${agent.id}** (${agent.specialty}) [${typeLabel}]\n` +
|
||||
` • Model: ${agent.model}\n` +
|
||||
` • Endpoint: ${agent.endpoint}\n` +
|
||||
` • Status: ${agent.status}\n` +
|
||||
` • Tasks: ${agent.current_tasks}/${agent.max_concurrent}\n`;
|
||||
};
|
||||
|
||||
let text = `📋 **Hive Cluster Agents** (${agents.length} total)\n\n`;
|
||||
|
||||
if (ollamaAgents.length > 0) {
|
||||
text += `🤖 **Ollama Agents** (${ollamaAgents.length}):\n`;
|
||||
text += ollamaAgents.map(formatAgent).join('\n') + '\n';
|
||||
}
|
||||
|
||||
if (cliAgents.length > 0) {
|
||||
text += `⚡ **CLI Agents** (${cliAgents.length}):\n`;
|
||||
text += cliAgents.map(formatAgent).join('\n') + '\n';
|
||||
}
|
||||
|
||||
if (agents.length === 0) {
|
||||
text += 'No agents registered yet.\n\n';
|
||||
text += '**Getting Started:**\n';
|
||||
text += '• Use `hive_register_agent` for Ollama agents\n';
|
||||
text += '• Use `hive_register_cli_agent` for CLI agents\n';
|
||||
text += '• Use `hive_register_predefined_cli_agents` for quick CLI setup\n';
|
||||
text += '• Use `hive_bring_online` for auto-discovery';
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `📋 Hive Cluster Agents (${agents.length} total):\n\n${agents.length > 0
|
||||
? agents.map(agent =>
|
||||
`🤖 **${agent.id}** (${agent.specialty})\n` +
|
||||
` • Model: ${agent.model}\n` +
|
||||
` • Endpoint: ${agent.endpoint}\n` +
|
||||
` • Status: ${agent.status}\n` +
|
||||
` • Tasks: ${agent.current_tasks}/${agent.max_concurrent}\n`
|
||||
).join('\n')
|
||||
: 'No agents registered yet. Use hive_register_agent to add agents to the cluster.'
|
||||
}`,
|
||||
text,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -662,4 +739,145 @@ export class HiveTools {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async registerCliAgent(args: any) {
|
||||
try {
|
||||
const result = await this.hiveClient.registerCliAgent(args);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `✅ **CLI Agent Registered Successfully!**\n\n` +
|
||||
`⚡ **Agent Details:**\n` +
|
||||
`• ID: **${args.id}**\n` +
|
||||
`• Host: ${args.host}\n` +
|
||||
`• Specialization: ${args.specialization}\n` +
|
||||
`• Model: ${args.model}\n` +
|
||||
`• Node Version: ${args.node_version}\n` +
|
||||
`• Max Concurrent: ${args.max_concurrent || 2}\n` +
|
||||
`• Endpoint: ${result.endpoint}\n\n` +
|
||||
`🔍 **Health Check:**\n` +
|
||||
`• SSH: ${result.health_check?.ssh_healthy ? '✅ Connected' : '❌ Failed'}\n` +
|
||||
`• CLI: ${result.health_check?.cli_healthy ? '✅ Working' : '❌ Failed'}\n` +
|
||||
`${result.health_check?.response_time ? `• Response Time: ${result.health_check.response_time.toFixed(2)}s\n` : ''}` +
|
||||
`\n🎯 **Ready for Tasks!** The CLI agent is now available for distributed AI coordination.`,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `❌ **Failed to register CLI agent**\n\n` +
|
||||
`Error: ${error instanceof Error ? error.message : String(error)}\n\n` +
|
||||
`**Troubleshooting:**\n` +
|
||||
`• Verify SSH connectivity to ${args.host}\n` +
|
||||
`• Ensure Gemini CLI is installed and accessible\n` +
|
||||
`• Check Node.js version ${args.node_version} is available\n` +
|
||||
`• Confirm Hive backend is running and accessible`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async getCliAgents() {
|
||||
try {
|
||||
const cliAgents = await this.hiveClient.getCliAgents();
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `⚡ **CLI Agents** (${cliAgents.length} total)\n\n${cliAgents.length > 0
|
||||
? cliAgents.map((agent: any) =>
|
||||
`⚡ **${agent.id}** (${agent.specialization})\n` +
|
||||
` • Model: ${agent.model}\n` +
|
||||
` • Host: ${agent.cli_config?.host || 'Unknown'}\n` +
|
||||
` • Node Version: ${agent.cli_config?.node_version || 'Unknown'}\n` +
|
||||
` • Status: ${agent.status}\n` +
|
||||
` • Tasks: ${agent.current_tasks}/${agent.max_concurrent}\n` +
|
||||
` • Endpoint: ${agent.endpoint}\n`
|
||||
).join('\n')
|
||||
: 'No CLI agents registered yet.\n\n' +
|
||||
'**Getting Started:**\n' +
|
||||
'• Use `hive_register_cli_agent` to register individual CLI agents\n' +
|
||||
'• Use `hive_register_predefined_cli_agents` to register walnut-gemini and ironwood-gemini automatically'
|
||||
}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `❌ **Failed to get CLI agents**\n\n` +
|
||||
`Error: ${error instanceof Error ? error.message : String(error)}\n\n` +
|
||||
`Please ensure the Hive backend is running and accessible.`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async registerPredefinedCliAgents() {
|
||||
try {
|
||||
const result = await this.hiveClient.registerPredefinedCliAgents();
|
||||
|
||||
const successCount = result.results.filter((r: any) => r.status === 'success').length;
|
||||
const existingCount = result.results.filter((r: any) => r.status === 'already_exists').length;
|
||||
const failedCount = result.results.filter((r: any) => r.status === 'failed').length;
|
||||
|
||||
let text = `⚡ **Predefined CLI Agents Registration Complete**\n\n`;
|
||||
text += `📊 **Summary:**\n`;
|
||||
text += `• Successfully registered: ${successCount}\n`;
|
||||
text += `• Already existed: ${existingCount}\n`;
|
||||
text += `• Failed: ${failedCount}\n\n`;
|
||||
|
||||
text += `📋 **Results:**\n`;
|
||||
for (const res of result.results) {
|
||||
const statusIcon = res.status === 'success' ? '✅' :
|
||||
res.status === 'already_exists' ? '📋' : '❌';
|
||||
text += `${statusIcon} **${res.agent_id}**: ${res.message || res.error || res.status}\n`;
|
||||
}
|
||||
|
||||
if (successCount > 0) {
|
||||
text += `\n🎯 **Ready for Action!** The CLI agents are now available for:\n`;
|
||||
text += `• General AI tasks (walnut-gemini)\n`;
|
||||
text += `• Advanced reasoning (ironwood-gemini)\n`;
|
||||
text += `• Mixed agent coordination\n`;
|
||||
text += `• Hybrid local/cloud AI orchestration`;
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `❌ **Failed to register predefined CLI agents**\n\n` +
|
||||
`Error: ${error instanceof Error ? error.message : String(error)}\n\n` +
|
||||
`**Troubleshooting:**\n` +
|
||||
`• Ensure WALNUT and IRONWOOD are accessible via SSH\n` +
|
||||
`• Verify Gemini CLI is installed on both machines\n` +
|
||||
`• Check that Node.js v22.14.0 (WALNUT) and v22.17.0 (IRONWOOD) are available\n` +
|
||||
`• Confirm Hive backend is running with CLI agent support`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user