feat(phase4): complete zclaw-skills, zclaw-hands, zclaw-channels, zclaw-protocols 模块实现

This commit is contained in:
iven
2026-03-22 08:57:37 +08:00
parent 7abfca9d5c
commit 0ab2f7afda
24 changed files with 2060 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
[package]
name = "zclaw-protocols"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
description = "ZCLAW protocol support (MCP, A2A)"
[dependencies]
zclaw-types = { workspace = true }
tokio = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
async-trait = { workspace = true }
reqwest = { workspace = true }

View File

@@ -0,0 +1,156 @@
//! A2A (Agent-to-Agent) protocol support
//!
//! Implements communication between AI agents.
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use zclaw_types::{Result, AgentId};
/// A2A message envelope
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct A2aEnvelope {
/// Message ID
pub id: String,
/// Sender agent ID
pub from: AgentId,
/// Recipient agent ID (or broadcast)
pub to: A2aRecipient,
/// Message type
pub message_type: A2aMessageType,
/// Message payload
pub payload: serde_json::Value,
/// Timestamp
pub timestamp: i64,
/// Conversation/thread ID
pub conversation_id: Option<String>,
/// Reply-to message ID
pub reply_to: Option<String>,
}
/// Recipient specification
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum A2aRecipient {
/// Direct message to specific agent
Direct { agent_id: AgentId },
/// Broadcast to all agents in a group
Group { group_id: String },
/// Broadcast to all agents
Broadcast,
}
/// A2A message types
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum A2aMessageType {
/// Request for information or action
Request,
/// Response to a request
Response,
/// Notification (no response expected)
Notification,
/// Error message
Error,
/// Heartbeat/ping
Heartbeat,
/// Capability advertisement
Capability,
}
/// Agent capability advertisement
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct A2aCapability {
/// Capability name
pub name: String,
/// Capability description
pub description: String,
/// Input schema
pub input_schema: Option<serde_json::Value>,
/// Output schema
pub output_schema: Option<serde_json::Value>,
/// Whether this capability requires approval
pub requires_approval: bool,
}
/// Agent profile for A2A
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct A2aAgentProfile {
/// Agent ID
pub id: AgentId,
/// Agent name
pub name: String,
/// Agent description
pub description: String,
/// Agent capabilities
pub capabilities: Vec<A2aCapability>,
/// Supported protocols
pub protocols: Vec<String>,
/// Agent metadata
pub metadata: HashMap<String, String>,
}
/// A2A client trait
#[async_trait]
pub trait A2aClient: Send + Sync {
/// Send a message to another agent
async fn send(&self, envelope: A2aEnvelope) -> Result<()>;
/// Receive messages (streaming)
async fn receive(&self) -> Result<tokio::sync::mpsc::Receiver<A2aEnvelope>>;
/// Get agent profile
async fn get_profile(&self, agent_id: &AgentId) -> Result<Option<A2aAgentProfile>>;
/// Discover agents with specific capabilities
async fn discover(&self, capability: &str) -> Result<Vec<A2aAgentProfile>>;
/// Advertise own capabilities
async fn advertise(&self, profile: A2aAgentProfile) -> Result<()>;
}
/// Basic A2A client implementation
pub struct BasicA2aClient {
agent_id: AgentId,
profiles: std::sync::Arc<tokio::sync::RwLock<HashMap<AgentId, A2aAgentProfile>>>,
}
impl BasicA2aClient {
pub fn new(agent_id: AgentId) -> Self {
Self {
agent_id,
profiles: std::sync::Arc::new(tokio::sync::RwLock::new(HashMap::new())),
}
}
}
#[async_trait]
impl A2aClient for BasicA2aClient {
async fn send(&self, _envelope: A2aEnvelope) -> Result<()> {
// TODO: Implement actual A2A protocol communication
tracing::info!("A2A send called");
Ok(())
}
async fn receive(&self) -> Result<tokio::sync::mpsc::Receiver<A2aEnvelope>> {
let (_tx, rx) = tokio::sync::mpsc::channel(100);
// TODO: Implement actual A2A protocol communication
Ok(rx)
}
async fn get_profile(&self, agent_id: &AgentId) -> Result<Option<A2aAgentProfile>> {
let profiles = self.profiles.read().await;
Ok(profiles.get(agent_id).cloned())
}
async fn discover(&self, _capability: &str) -> Result<Vec<A2aAgentProfile>> {
let profiles = self.profiles.read().await;
Ok(profiles.values().cloned().collect())
}
async fn advertise(&self, profile: A2aAgentProfile) -> Result<()> {
let mut profiles = self.profiles.write().await;
profiles.insert(profile.id.clone(), profile);
Ok(())
}
}

View File

@@ -0,0 +1,9 @@
//! ZCLAW Protocols
//!
//! Protocol support for MCP (Model Context Protocol) and A2A (Agent-to-Agent).
mod mcp;
mod a2a;
pub use mcp::*;
pub use a2a::*;

View File

@@ -0,0 +1,183 @@
//! MCP (Model Context Protocol) support
//!
//! Implements MCP client and server for tool/resource integration.
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use zclaw_types::Result;
/// MCP tool definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpTool {
pub name: String,
pub description: String,
pub input_schema: serde_json::Value,
}
/// MCP resource definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpResource {
pub uri: String,
pub name: String,
pub description: Option<String>,
pub mime_type: Option<String>,
}
/// MCP prompt definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpPrompt {
pub name: String,
pub description: String,
pub arguments: Vec<McpPromptArgument>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpPromptArgument {
pub name: String,
pub description: String,
pub required: bool,
}
/// MCP server info
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpServerInfo {
pub name: String,
pub version: String,
pub protocol_version: String,
}
/// MCP client configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpClientConfig {
pub server_url: String,
pub server_info: McpServerInfo,
pub capabilities: McpCapabilities,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct McpCapabilities {
pub tools: Option<McpToolCapabilities>,
pub resources: Option<McpResourceCapabilities>,
pub prompts: Option<McpPromptCapabilities>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpToolCapabilities {
pub list_changed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpResourceCapabilities {
pub subscribe: bool,
pub list_changed: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpPromptCapabilities {
pub list_changed: bool,
}
/// MCP tool call request
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpToolCallRequest {
pub name: String,
pub arguments: HashMap<String, serde_json::Value>,
}
/// MCP tool call response
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpToolCallResponse {
pub content: Vec<McpContent>,
pub is_error: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum McpContent {
Text { text: String },
Image { data: String, mime_type: String },
Resource { resource: McpResourceContent },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpResourceContent {
pub uri: String,
pub mime_type: Option<String>,
pub text: Option<String>,
pub blob: Option<String>,
}
/// MCP Client trait
#[async_trait]
pub trait McpClient: Send + Sync {
/// List available tools
async fn list_tools(&self) -> Result<Vec<McpTool>>;
/// Call a tool
async fn call_tool(&self, request: McpToolCallRequest) -> Result<McpToolCallResponse>;
/// List available resources
async fn list_resources(&self) -> Result<Vec<McpResource>>;
/// Read a resource
async fn read_resource(&self, uri: &str) -> Result<McpResourceContent>;
/// List available prompts
async fn list_prompts(&self) -> Result<Vec<McpPrompt>>;
/// Get a prompt
async fn get_prompt(&self, name: &str, arguments: HashMap<String, String>) -> Result<String>;
}
/// Basic MCP client implementation
pub struct BasicMcpClient {
config: McpClientConfig,
client: reqwest::Client,
}
impl BasicMcpClient {
pub fn new(config: McpClientConfig) -> Self {
Self {
config,
client: reqwest::Client::new(),
}
}
}
#[async_trait]
impl McpClient for BasicMcpClient {
async fn list_tools(&self) -> Result<Vec<McpTool>> {
// TODO: Implement actual MCP protocol communication
Ok(Vec::new())
}
async fn call_tool(&self, _request: McpToolCallRequest) -> Result<McpToolCallResponse> {
// TODO: Implement actual MCP protocol communication
Ok(McpToolCallResponse {
content: vec![McpContent::Text { text: "Not implemented".to_string() }],
is_error: true,
})
}
async fn list_resources(&self) -> Result<Vec<McpResource>> {
Ok(Vec::new())
}
async fn read_resource(&self, _uri: &str) -> Result<McpResourceContent> {
Ok(McpResourceContent {
uri: String::new(),
mime_type: None,
text: Some("Not implemented".to_string()),
blob: None,
})
}
async fn list_prompts(&self) -> Result<Vec<McpPrompt>> {
Ok(Vec::new())
}
async fn get_prompt(&self, _name: &str, _arguments: HashMap<String, String>) -> Result<String> {
Ok("Not implemented".to_string())
}
}