Prepare for v2 development: Add MCP integration and future development planning

- Add FUTURE_DEVELOPMENT.md with comprehensive v2 protocol specification
- Add MCP integration design and implementation foundation
- Add infrastructure and deployment configurations
- Update system architecture for v2 evolution

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-08-07 14:38:22 +10:00
parent 5f94288fbb
commit 065dddf8d5
41 changed files with 14970 additions and 161 deletions

View File

@@ -0,0 +1,493 @@
import { Logger } from '../utils/logger.js';
import { AgentManager } from '../agents/agent-manager.js';
import { ConversationManager } from '../conversations/conversation-manager.js';
import { BzzzP2PConnector } from '../p2p/bzzz-connector.js';
export interface SemanticAddress {
agent?: string;
role?: string;
project?: string;
task?: string;
path?: string;
raw: string;
}
export interface ProtocolToolsConfig {
agentManager: AgentManager;
p2pConnector: BzzzP2PConnector;
conversationManager: ConversationManager;
}
/**
* BzzzProtocolTools implements the core BZZZ protocol operations as MCP tools
*/
export class BzzzProtocolTools {
private logger: Logger;
private agentManager: AgentManager;
private p2pConnector: BzzzP2PConnector;
private conversationManager: ConversationManager;
constructor(config: ProtocolToolsConfig) {
this.logger = new Logger('BzzzProtocolTools');
this.agentManager = config.agentManager;
this.p2pConnector = config.p2pConnector;
this.conversationManager = config.conversationManager;
}
/**
* Handle bzzz_announce - Agent presence announcement
*/
async handleAnnounce(args: Record<string, any>): Promise<any> {
const { agent_id, role, capabilities, specialization, max_tasks = 3 } = args;
if (!agent_id || !role) {
throw new Error('agent_id and role are required for announcement');
}
this.logger.info(`Announcing agent ${agent_id} with role ${role}`);
try {
// Create or update agent
const agent = await this.agentManager.createAgent({
id: agent_id,
role,
capabilities: capabilities || [],
specialization: specialization || role,
maxTasks: max_tasks,
});
// Announce to P2P network
const announcement = {
type: 'capability_broadcast',
agent_id,
role,
capabilities: capabilities || [],
specialization: specialization || role,
max_tasks,
timestamp: new Date().toISOString(),
network_address: this.p2pConnector.getNodeId(),
};
await this.p2pConnector.publishMessage('bzzz/coordination/v1', announcement);
return {
success: true,
message: `Agent ${agent_id} (${role}) announced to BZZZ network`,
agent: {
id: agent.id,
role: agent.role,
capabilities: agent.capabilities,
specialization: agent.specialization,
status: agent.status,
},
};
} catch (error) {
this.logger.error('Failed to announce agent:', error);
throw new Error(`Announcement failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Handle bzzz_lookup - Semantic address discovery
*/
async handleLookup(args: Record<string, any>): Promise<any> {
const { semantic_address, filter_criteria = {} } = args;
if (!semantic_address) {
throw new Error('semantic_address is required for lookup');
}
this.logger.info(`Looking up semantic address: ${semantic_address}`);
try {
// Parse semantic address
const address = this.parseSemanticAddress(semantic_address);
// Discover matching agents
const agents = await this.discoverAgents(address, filter_criteria);
// Query P2P network for additional matches
const networkResults = await this.queryP2PNetwork(address);
// Combine and rank results
const allMatches = [...agents, ...networkResults];
const rankedMatches = this.rankMatches(allMatches, address, filter_criteria);
return {
success: true,
address: semantic_address,
parsed_address: address,
matches: rankedMatches,
count: rankedMatches.length,
query_time: new Date().toISOString(),
};
} catch (error) {
this.logger.error('Failed to lookup address:', error);
throw new Error(`Lookup failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Handle bzzz_get - Content retrieval from addresses
*/
async handleGet(args: Record<string, any>): Promise<any> {
const { address, include_metadata = true, max_history = 10 } = args;
if (!address) {
throw new Error('address is required for get operation');
}
this.logger.info(`Getting content from address: ${address}`);
try {
const parsedAddress = this.parseSemanticAddress(address);
// Retrieve content based on address type
let content;
let metadata = {};
if (parsedAddress.agent) {
// Get agent-specific content
content = await this.getAgentContent(parsedAddress, max_history);
if (include_metadata) {
metadata = await this.getAgentMetadata(parsedAddress.agent);
}
} else if (parsedAddress.project) {
// Get project-specific content
content = await this.getProjectContent(parsedAddress, max_history);
if (include_metadata) {
metadata = await this.getProjectMetadata(parsedAddress.project);
}
} else {
// General network query
content = await this.getNetworkContent(parsedAddress, max_history);
}
return {
success: true,
address,
content,
metadata: include_metadata ? metadata : undefined,
retrieved_at: new Date().toISOString(),
};
} catch (error) {
this.logger.error('Failed to get content:', error);
throw new Error(`Get operation failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Handle bzzz_post - Event/message posting
*/
async handlePost(args: Record<string, any>): Promise<any> {
const { target_address, message_type, content, priority = 'medium', thread_id } = args;
if (!target_address || !message_type || !content) {
throw new Error('target_address, message_type, and content are required for post operation');
}
this.logger.info(`Posting ${message_type} to address: ${target_address}`);
try {
const parsedAddress = this.parseSemanticAddress(target_address);
// Create message payload
const message = {
type: message_type,
content,
priority,
thread_id,
target_address,
sender_id: this.p2pConnector.getNodeId(),
timestamp: new Date().toISOString(),
parsed_address: parsedAddress,
};
// Determine routing strategy
let deliveryResults;
if (parsedAddress.agent) {
// Direct agent messaging
deliveryResults = await this.postToAgent(parsedAddress.agent, message);
} else if (parsedAddress.role) {
// Role-based broadcasting
deliveryResults = await this.postToRole(parsedAddress.role, message);
} else if (parsedAddress.project) {
// Project-specific messaging
deliveryResults = await this.postToProject(parsedAddress.project, message);
} else {
// General network broadcast
deliveryResults = await this.postToNetwork(message);
}
return {
success: true,
message_id: this.generateMessageId(),
target_address,
message_type,
delivery_results: deliveryResults,
posted_at: new Date().toISOString(),
};
} catch (error) {
this.logger.error('Failed to post message:', error);
throw new Error(`Post operation failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Handle bzzz_thread - Conversation management
*/
async handleThread(args: Record<string, any>): Promise<any> {
const { action, thread_id, participants, topic } = args;
if (!action) {
throw new Error('action is required for thread operation');
}
this.logger.info(`Thread action: ${action}, thread_id: ${thread_id}`);
try {
let result;
switch (action) {
case 'create':
if (!topic || !participants?.length) {
throw new Error('topic and participants are required for creating threads');
}
result = await this.conversationManager.createThread({
topic,
participants,
creator: this.p2pConnector.getNodeId(),
});
break;
case 'join':
if (!thread_id) {
throw new Error('thread_id is required for joining threads');
}
result = await this.conversationManager.joinThread(
thread_id,
this.p2pConnector.getNodeId()
);
break;
case 'leave':
if (!thread_id) {
throw new Error('thread_id is required for leaving threads');
}
result = await this.conversationManager.leaveThread(
thread_id,
this.p2pConnector.getNodeId()
);
break;
case 'list':
result = await this.conversationManager.listThreads(
this.p2pConnector.getNodeId()
);
break;
case 'summarize':
if (!thread_id) {
throw new Error('thread_id is required for summarizing threads');
}
result = await this.conversationManager.summarizeThread(thread_id);
break;
default:
throw new Error(`Unknown thread action: ${action}`);
}
return {
success: true,
action,
thread_id,
result,
timestamp: new Date().toISOString(),
};
} catch (error) {
this.logger.error('Thread operation failed:', error);
throw new Error(`Thread operation failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Handle bzzz_subscribe - Real-time event subscription
*/
async handleSubscribe(args: Record<string, any>): Promise<any> {
const { event_types, filter_address, callback_webhook } = args;
if (!event_types?.length) {
throw new Error('event_types is required for subscription');
}
this.logger.info(`Subscribing to events: ${event_types.join(', ')}`);
try {
const subscription = await this.p2pConnector.subscribe({
eventTypes: event_types,
filterAddress: filter_address,
callbackWebhook: callback_webhook,
subscriberId: this.p2pConnector.getNodeId(),
});
return {
success: true,
subscription_id: subscription.id,
event_types,
filter_address,
callback_webhook,
subscribed_at: new Date().toISOString(),
status: 'active',
};
} catch (error) {
this.logger.error('Failed to create subscription:', error);
throw new Error(`Subscription failed: ${error instanceof Error ? error.message : String(error)}`);
}
}
// Helper methods
private parseSemanticAddress(address: string): SemanticAddress {
// Parse bzzz://agent:role@project:task/path
const bzzzMatch = address.match(/^bzzz:\/\/([^@\/]+)@([^\/]+)(?:\/(.+))?$/);
if (bzzzMatch) {
const [, agentRole, projectTask, path] = bzzzMatch;
const [agent, role] = agentRole.split(':');
const [project, task] = projectTask.split(':');
return {
agent: agent !== '*' ? agent : undefined,
role: role !== '*' ? role : undefined,
project: project !== '*' ? project : undefined,
task: task !== '*' ? task : undefined,
path: path || undefined,
raw: address,
};
}
// Simple address format
return { raw: address };
}
private async discoverAgents(address: SemanticAddress, criteria: any): Promise<any[]> {
const agents = await this.agentManager.getAgents();
return agents.filter(agent => {
if (address.agent && agent.id !== address.agent) return false;
if (address.role && agent.role !== address.role) return false;
if (criteria.availability && !agent.available) return false;
if (criteria.performance_threshold && agent.performance < criteria.performance_threshold) return false;
if (criteria.expertise?.length && !criteria.expertise.some((exp: string) =>
agent.capabilities.includes(exp))) return false;
return true;
});
}
private async queryP2PNetwork(address: SemanticAddress): Promise<any[]> {
// Query the P2P network for matching agents
const query = {
type: 'agent_discovery',
criteria: address,
timestamp: new Date().toISOString(),
};
const responses = await this.p2pConnector.queryNetwork(query, 5000); // 5 second timeout
return responses;
}
private rankMatches(matches: any[], address: SemanticAddress, criteria: any): any[] {
return matches
.map(match => ({
...match,
score: this.calculateMatchScore(match, address, criteria),
}))
.sort((a, b) => b.score - a.score)
.slice(0, 20); // Limit to top 20 matches
}
private calculateMatchScore(match: any, address: SemanticAddress, criteria: any): number {
let score = 0;
// Exact matches get highest score
if (address.agent && match.id === address.agent) score += 100;
if (address.role && match.role === address.role) score += 50;
// Capability matching
if (criteria.expertise?.length) {
const matchingExp = criteria.expertise.filter((exp: string) =>
match.capabilities?.includes(exp)
).length;
score += (matchingExp / criteria.expertise.length) * 30;
}
// Availability bonus
if (match.available) score += 10;
// Performance bonus
if (match.performance) score += match.performance * 10;
return score;
}
private async getAgentContent(address: SemanticAddress, maxHistory: number): Promise<any> {
const agent = await this.agentManager.getAgent(address.agent!);
if (!agent) {
throw new Error(`Agent ${address.agent} not found`);
}
const content = {
agent_info: agent,
recent_activity: await this.agentManager.getRecentActivity(address.agent!, maxHistory),
current_tasks: await this.agentManager.getCurrentTasks(address.agent!),
};
if (address.path) {
content[address.path] = await this.agentManager.getAgentData(address.agent!, address.path);
}
return content;
}
private async getProjectContent(address: SemanticAddress, maxHistory: number): Promise<any> {
// Get project-related content from P2P network
return await this.p2pConnector.getProjectData(address.project!, maxHistory);
}
private async getNetworkContent(address: SemanticAddress, maxHistory: number): Promise<any> {
// Get general network content
return await this.p2pConnector.getNetworkData(address.raw, maxHistory);
}
private async getAgentMetadata(agentId: string): Promise<any> {
return await this.agentManager.getAgentMetadata(agentId);
}
private async getProjectMetadata(projectId: string): Promise<any> {
return await this.p2pConnector.getProjectMetadata(projectId);
}
private async postToAgent(agentId: string, message: any): Promise<any> {
return await this.p2pConnector.sendDirectMessage(agentId, message);
}
private async postToRole(role: string, message: any): Promise<any> {
const topic = `bzzz/roles/${role.toLowerCase().replace(/\s+/g, '_')}/v1`;
return await this.p2pConnector.publishMessage(topic, message);
}
private async postToProject(projectId: string, message: any): Promise<any> {
const topic = `bzzz/projects/${projectId}/coordination/v1`;
return await this.p2pConnector.publishMessage(topic, message);
}
private async postToNetwork(message: any): Promise<any> {
return await this.p2pConnector.publishMessage('bzzz/coordination/v1', message);
}
private generateMessageId(): string {
return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}