fix(runtime): 修复 Skill/MCP 调用链路3个断点
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
1. Anthropic Driver ToolResult 格式修复 — ContentBlock 添加 ToolResult 变体, tool_call_id 不再被丢弃, 按 Anthropic API 规范发送 tool_result 格式 2. 前端 callMcpTool 参数名对齐 — serviceName/toolName/args 改为 service_name/tool_name/arguments, 后端支持 service_name 精确路由 3. MCP 工具桥接到 ToolRegistry — McpToolAdapter 添加 service_name/clone, 新建 McpToolWrapper 实现 Tool trait, Kernel 添加 mcp_adapters 共享状态, McpManagerState 与 Kernel 共享同一 Arc<RwLock<Vec>>, MCP 服务启停时 自动同步工具列表到 LLM 可见的 ToolRegistry
This commit is contained in:
@@ -231,15 +231,19 @@ impl AnthropicDriver {
|
||||
input: input.clone(),
|
||||
}],
|
||||
}),
|
||||
zclaw_types::Message::ToolResult { tool_call_id: _, tool: _, output, is_error } => {
|
||||
let content = if *is_error {
|
||||
zclaw_types::Message::ToolResult { tool_call_id, tool: _, output, is_error } => {
|
||||
let content_text = if *is_error {
|
||||
format!("Error: {}", output)
|
||||
} else {
|
||||
output.to_string()
|
||||
};
|
||||
Some(AnthropicMessage {
|
||||
role: "user".to_string(),
|
||||
content: vec![ContentBlock::Text { text: content }],
|
||||
content: vec![ContentBlock::ToolResult {
|
||||
tool_use_id: tool_call_id.clone(),
|
||||
content: content_text,
|
||||
is_error: *is_error,
|
||||
}],
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
|
||||
@@ -116,6 +116,13 @@ pub enum ContentBlock {
|
||||
Text { text: String },
|
||||
Thinking { thinking: String },
|
||||
ToolUse { id: String, name: String, input: serde_json::Value },
|
||||
/// Anthropic API tool result — must be sent as `role: "user"` with this content block.
|
||||
ToolResult {
|
||||
tool_use_id: String,
|
||||
content: String,
|
||||
#[serde(skip_serializing_if = "std::ops::Not::not")]
|
||||
is_error: bool,
|
||||
},
|
||||
}
|
||||
|
||||
/// Stop reason
|
||||
|
||||
@@ -737,6 +737,9 @@ impl OpenAiDriver {
|
||||
input: input.clone(),
|
||||
});
|
||||
}
|
||||
ContentBlock::ToolResult { .. } => {
|
||||
// ToolResult is only used in request messages, never in responses
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ mod skill_load;
|
||||
mod path_validator;
|
||||
mod task;
|
||||
mod ask_clarification;
|
||||
pub mod mcp_tool;
|
||||
|
||||
pub use file_read::FileReadTool;
|
||||
pub use file_write::FileWriteTool;
|
||||
@@ -19,6 +20,7 @@ pub use skill_load::SkillLoadTool;
|
||||
pub use path_validator::{PathValidator, PathValidatorConfig};
|
||||
pub use task::TaskTool;
|
||||
pub use ask_clarification::AskClarificationTool;
|
||||
pub use mcp_tool::McpToolWrapper;
|
||||
|
||||
use crate::tool::ToolRegistry;
|
||||
|
||||
|
||||
48
crates/zclaw-runtime/src/tool/builtin/mcp_tool.rs
Normal file
48
crates/zclaw-runtime/src/tool/builtin/mcp_tool.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
//! MCP Tool Wrapper — bridges MCP server tools into the ToolRegistry
|
||||
//!
|
||||
//! Wraps `McpToolAdapter` (from zclaw-protocols) as a `Tool` trait object
|
||||
//! so the LLM can discover and call MCP tools during conversations.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde_json::Value;
|
||||
use std::sync::Arc;
|
||||
use zclaw_types::Result;
|
||||
|
||||
use crate::tool::{Tool, ToolContext};
|
||||
|
||||
/// Wraps an MCP tool adapter into the `Tool` trait.
|
||||
///
|
||||
/// The wrapper holds an `Arc<McpToolAdapter>` and delegates execution
|
||||
/// to the adapter, ignoring the `ToolContext` (MCP tools don't need
|
||||
/// agent_id, workspace, etc.).
|
||||
pub struct McpToolWrapper {
|
||||
adapter: Arc<zclaw_protocols::McpToolAdapter>,
|
||||
/// Cached qualified name (service.tool) for Tool::name()
|
||||
qualified_name: String,
|
||||
}
|
||||
|
||||
impl McpToolWrapper {
|
||||
pub fn new(adapter: Arc<zclaw_protocols::McpToolAdapter>) -> Self {
|
||||
let qualified_name = adapter.qualified_name();
|
||||
Self { adapter, qualified_name }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Tool for McpToolWrapper {
|
||||
fn name(&self) -> &str {
|
||||
&self.qualified_name
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
self.adapter.description()
|
||||
}
|
||||
|
||||
fn input_schema(&self) -> Value {
|
||||
self.adapter.input_schema().clone()
|
||||
}
|
||||
|
||||
async fn execute(&self, input: Value, _context: &ToolContext) -> Result<Value> {
|
||||
self.adapter.execute(input).await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user