feat(phase4): complete zclaw-skills, zclaw-hands, zclaw-channels, zclaw-protocols 模块实现
This commit is contained in:
19
crates/zclaw-protocols/Cargo.toml
Normal file
19
crates/zclaw-protocols/Cargo.toml
Normal 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 }
|
||||
156
crates/zclaw-protocols/src/a2a.rs
Normal file
156
crates/zclaw-protocols/src/a2a.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
9
crates/zclaw-protocols/src/lib.rs
Normal file
9
crates/zclaw-protocols/src/lib.rs
Normal 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::*;
|
||||
183
crates/zclaw-protocols/src/mcp.rs
Normal file
183
crates/zclaw-protocols/src/mcp.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user