fix(agent): improve ReAct parser and tool schemas for better LLM compatibility
- Fix ACTION_INPUT regex to properly capture multiline JSON responses - Changed from stopping at first newline to capturing all remaining text - Resolves parsing errors when LLM generates formatted JSON with line breaks - Enhance tool schemas with detailed descriptions and parameter specifications - Add comprehensive Message schema for generate_text tool - Clarify distinction between resources/get (file read) and resources/list (directory listing) - Include clear usage guidance in tool descriptions - Set default model to llama3.2:latest instead of invalid "ollama" - Add parse error debugging to help troubleshoot LLM response issues The agent infrastructure now correctly handles multiline tool arguments and provides better guidance to LLMs through improved tool schemas. Remaining errors are due to LLM quality (model making poor tool choices or generating malformed responses), not infrastructure bugs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
113
crates/owlen-core/src/mcp.rs
Normal file
113
crates/owlen-core/src/mcp.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use crate::tools::registry::ToolRegistry;
|
||||
use crate::validation::SchemaValidator;
|
||||
use crate::Result;
|
||||
use async_trait::async_trait;
|
||||
pub use client::McpClient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
pub mod client;
|
||||
pub mod factory;
|
||||
pub mod permission;
|
||||
pub mod protocol;
|
||||
pub mod remote_client;
|
||||
|
||||
/// Descriptor for a tool exposed over MCP
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct McpToolDescriptor {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub input_schema: Value,
|
||||
pub requires_network: bool,
|
||||
pub requires_filesystem: Vec<String>,
|
||||
}
|
||||
|
||||
/// Invocation payload for a tool call
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct McpToolCall {
|
||||
pub name: String,
|
||||
pub arguments: Value,
|
||||
}
|
||||
|
||||
/// Result returned by a tool invocation
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct McpToolResponse {
|
||||
pub name: String,
|
||||
pub success: bool,
|
||||
pub output: Value,
|
||||
pub metadata: HashMap<String, String>,
|
||||
pub duration_ms: u128,
|
||||
}
|
||||
|
||||
/// Thin MCP server facade over the tool registry
|
||||
pub struct McpServer {
|
||||
registry: Arc<ToolRegistry>,
|
||||
validator: Arc<SchemaValidator>,
|
||||
}
|
||||
|
||||
impl McpServer {
|
||||
pub fn new(registry: Arc<ToolRegistry>, validator: Arc<SchemaValidator>) -> Self {
|
||||
Self {
|
||||
registry,
|
||||
validator,
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumerate the registered tools as MCP descriptors
|
||||
pub fn list_tools(&self) -> Vec<McpToolDescriptor> {
|
||||
self.registry
|
||||
.all()
|
||||
.into_iter()
|
||||
.map(|tool| McpToolDescriptor {
|
||||
name: tool.name().to_string(),
|
||||
description: tool.description().to_string(),
|
||||
input_schema: tool.schema(),
|
||||
requires_network: tool.requires_network(),
|
||||
requires_filesystem: tool.requires_filesystem(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Execute a tool call after validating inputs against the registered schema
|
||||
pub async fn call_tool(&self, call: McpToolCall) -> Result<McpToolResponse> {
|
||||
self.validator.validate(&call.name, &call.arguments)?;
|
||||
let result = self.registry.execute(&call.name, call.arguments).await?;
|
||||
Ok(McpToolResponse {
|
||||
name: call.name,
|
||||
success: result.success,
|
||||
output: result.output,
|
||||
metadata: result.metadata,
|
||||
duration_ms: duration_to_millis(result.duration),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn duration_to_millis(duration: Duration) -> u128 {
|
||||
duration.as_secs() as u128 * 1_000 + u128::from(duration.subsec_millis())
|
||||
}
|
||||
|
||||
pub struct LocalMcpClient {
|
||||
server: McpServer,
|
||||
}
|
||||
|
||||
impl LocalMcpClient {
|
||||
pub fn new(registry: Arc<ToolRegistry>, validator: Arc<SchemaValidator>) -> Self {
|
||||
Self {
|
||||
server: McpServer::new(registry, validator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl McpClient for LocalMcpClient {
|
||||
async fn list_tools(&self) -> Result<Vec<McpToolDescriptor>> {
|
||||
Ok(self.server.list_tools())
|
||||
}
|
||||
|
||||
async fn call_tool(&self, call: McpToolCall) -> Result<McpToolResponse> {
|
||||
self.server.call_tool(call).await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user