use async_trait::async_trait;
use enki_runtime::core::action::{ActionInvoker, ActionMetadata, ToolInvoker};
use enki_runtime::core::agent::{Agent, AgentContext};
use enki_runtime::core::error::Result;
use enki_runtime::core::message::Message;
use enki_runtime::mcp::McpAgentServer;
use serde_json::{json, Value};
struct MathAgent {
name: String,
tool_invoker: ToolInvoker,
}
impl MathAgent {
fn new() -> Self {
let mut tool_invoker = ToolInvoker::new();
tool_invoker.register(Box::new(AddAction::new()));
tool_invoker.register(Box::new(GreetAction::new()));
Self {
name: "math-agent".to_string(),
tool_invoker,
}
}
}
#[async_trait]
impl Agent for MathAgent {
fn name(&self) -> String {
self.name.clone()
}
async fn on_message(&mut self, _msg: Message, _ctx: &mut AgentContext) -> Result<()> {
Ok(())
}
fn tool_invoker(&self) -> Option<&ToolInvoker> {
Some(&self.tool_invoker)
}
fn tool_invoker_mut(&mut self) -> Option<&mut ToolInvoker> {
Some(&mut self.tool_invoker)
}
}
struct AddAction {
metadata: ActionMetadata,
}
impl AddAction {
fn new() -> Self {
Self {
metadata: ActionMetadata {
name: "add".to_string(),
description: "Add two numbers together".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"a": { "type": "number" },
"b": { "type": "number" }
},
"required": ["a", "b"]
}),
output_schema: Some(json!({ "type": "number" })),
},
}
}
}
#[async_trait]
impl ActionInvoker for AddAction {
async fn execute(&self, _ctx: &mut AgentContext, inputs: Value) -> Result<Value> {
let a = inputs["a"].as_f64().unwrap_or(0.0);
let b = inputs["b"].as_f64().unwrap_or(0.0);
Ok(json!(a + b))
}
fn metadata(&self) -> &ActionMetadata {
&self.metadata
}
}
struct GreetAction {
metadata: ActionMetadata,
}
impl GreetAction {
fn new() -> Self {
Self {
metadata: ActionMetadata {
name: "greet".to_string(),
description: "Generate a greeting message".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"name": { "type": "string" }
},
"required": ["name"]
}),
output_schema: Some(json!({ "type": "string" })),
},
}
}
}
#[async_trait]
impl ActionInvoker for GreetAction {
async fn execute(&self, _ctx: &mut AgentContext, inputs: Value) -> Result<Value> {
let name = inputs["name"].as_str().unwrap_or("World");
Ok(json!(format!("Hello, {}!", name)))
}
fn metadata(&self) -> &ActionMetadata {
&self.metadata
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
println!("=== MCP Self-Contained Test ===\n");
let agent = MathAgent::new();
println!("✓ Created MathAgent with tools");
if let Some(invoker) = agent.tool_invoker() {
let actions = invoker.list_actions();
println!("✓ Registered {} tools:", actions.len());
for action in &actions {
println!(" - {}: {}", action.name, action.description);
}
}
println!("\n--- Testing Direct Tool Invocation ---");
let mut ctx = AgentContext::new("test".to_string(), None);
if let Some(invoker) = agent.tool_invoker() {
let result = invoker
.invoke("add", &mut ctx, json!({"a": 5, "b": 3}))
.await?;
println!("✓ add(5, 3) = {}", result);
assert_eq!(result, json!(8.0));
let result = invoker
.invoke("greet", &mut ctx, json!({"name": "Enki"}))
.await?;
println!("✓ greet(\"Enki\") = {}", result);
assert_eq!(result, json!("Hello, Enki!"));
}
println!("\n--- Testing MCP Server Wrapper ---");
let mcp_server = McpAgentServer::new(agent, "test-mcp-server");
println!("✓ Created McpAgentServer: {:?}", mcp_server);
println!("\n--- Testing Type Conversions ---");
test_type_conversions();
println!("\n=== All Tests Passed! ===");
Ok(())
}
fn test_type_conversions() {
use enki_runtime::mcp::types::{action_to_mcp_tool, mcp_tool_to_action_metadata};
let action = ActionMetadata {
name: "test_tool".to_string(),
description: "A test tool".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"input": { "type": "string" }
}
}),
output_schema: Some(json!({ "type": "string" })),
};
let mcp_tool = action_to_mcp_tool(&action);
println!("✓ Converted ActionMetadata -> MCP Tool");
println!(" Name: {}", mcp_tool.name);
println!(" Description: {:?}", mcp_tool.description);
let action_back = mcp_tool_to_action_metadata(&mcp_tool);
println!("✓ Converted MCP Tool -> ActionMetadata");
assert_eq!(action.name, action_back.name);
assert_eq!(action.description, action_back.description);
println!("✓ Roundtrip conversion verified");
}