Completes Phase 4 (Agentic Loop with ReAct), Phase 7 (Code Execution), and Phase 8 (Prompt Server) as specified in the implementation plan. **Phase 4: Agentic Loop with ReAct Pattern (agent.rs - 398 lines)** - Complete AgentExecutor with reasoning loop - LlmResponse enum: ToolCall, FinalAnswer, Reasoning - ReAct parser supporting THOUGHT/ACTION/ACTION_INPUT/FINAL_ANSWER - Tool discovery and execution integration - AgentResult with iteration tracking and message history - Integration with owlen-agent CLI binary and TUI **Phase 7: Code Execution with Docker Sandboxing** *Sandbox Module (sandbox.rs - 255 lines):* - Docker-based execution using bollard - Resource limits: 512MB memory, 50% CPU - Network isolation (no network access) - Timeout handling (30s default) - Container auto-cleanup - Support for Rust, Node.js, Python environments *Tool Suite (tools.rs - 410 lines):* - CompileProjectTool: Build projects with auto-detection - RunTestsTool: Execute test suites with optional filters - FormatCodeTool: Run formatters (rustfmt/prettier/black) - LintCodeTool: Run linters (clippy/eslint/pylint) - All tools support check-only and auto-fix modes *MCP Server (lib.rs - 183 lines):* - Full JSON-RPC protocol implementation - Tool registry with dynamic dispatch - Initialize/tools/list/tools/call support **Phase 8: Prompt Server with YAML & Handlebars** *Prompt Server (lib.rs - 405 lines):* - YAML-based template storage in ~/.config/owlen/prompts/ - Handlebars 6.0 template engine integration - PromptTemplate with metadata (name, version, mode, description) - Four MCP tools: - get_prompt: Retrieve template by name - render_prompt: Render with Handlebars variables - list_prompts: List all available templates - reload_prompts: Hot-reload from disk *Default Templates:* - chat_mode_system.yaml: ReAct prompt for chat mode - code_mode_system.yaml: ReAct prompt with code tools **Configuration & Integration:** - Added Agent module to owlen-core - Updated owlen-agent binary to use new AgentExecutor API - Updated TUI to integrate with agent result structure - Added error handling for Agent variant **Dependencies Added:** - bollard 0.17 (Docker API) - handlebars 6.0 (templating) - serde_yaml 0.9 (YAML parsing) - tempfile 3.0 (temporary directories) - uuid 1.0 with v4 feature **Tests:** - mode_tool_filter.rs: Tool filtering by mode - prompt_server.rs: Prompt management tests - Sandbox tests (Docker-dependent, marked #[ignore]) All code compiles successfully and follows project conventions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
51 lines
1.6 KiB
Rust
51 lines
1.6 KiB
Rust
//! Integration test for the MCP prompt rendering server.
|
|
|
|
use owlen_core::config::McpServerConfig;
|
|
use owlen_core::mcp::client::RemoteMcpClient;
|
|
use owlen_core::mcp::{McpToolCall, McpToolResponse};
|
|
use owlen_core::Result;
|
|
use serde_json::json;
|
|
use std::path::PathBuf;
|
|
|
|
#[tokio::test]
|
|
async fn test_render_prompt_via_external_server() -> Result<()> {
|
|
// Locate the compiled prompt server binary.
|
|
let mut binary = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
binary.pop(); // remove `tests`
|
|
binary.pop(); // remove `owlen-core`
|
|
binary.push("owlen-mcp-prompt-server");
|
|
binary.push("target");
|
|
binary.push("debug");
|
|
binary.push("owlen-mcp-prompt-server");
|
|
assert!(
|
|
binary.exists(),
|
|
"Prompt server binary not found: {:?}",
|
|
binary
|
|
);
|
|
|
|
let config = McpServerConfig {
|
|
name: "prompt_server".into(),
|
|
command: binary.to_string_lossy().into_owned(),
|
|
args: Vec::new(),
|
|
transport: "stdio".into(),
|
|
env: std::collections::HashMap::new(),
|
|
};
|
|
|
|
let client = RemoteMcpClient::new_with_config(&config)?;
|
|
|
|
let call = McpToolCall {
|
|
name: "render_prompt".into(),
|
|
arguments: json!({
|
|
"template_name": "example",
|
|
"variables": {"name": "Alice", "role": "Tester"}
|
|
}),
|
|
};
|
|
|
|
let resp: McpToolResponse = client.call_tool(call).await?;
|
|
assert!(resp.success, "Tool reported failure: {:?}", resp);
|
|
let output = resp.output.as_str().unwrap_or("");
|
|
assert!(output.contains("Alice"), "Output missing name: {}", output);
|
|
assert!(output.contains("Tester"), "Output missing role: {}", output);
|
|
Ok(())
|
|
}
|