pub mod execution;
pub mod registry;
pub mod schema;
pub use execution::{ToolContext, ToolHandler, ToolOutcome, validate_against_schema};
pub use registry::ToolRegistry;
pub use schema::{Schema, SchemaGenerator};
pub const AGENT_TOOL_INVOKE_PATH: &str = "/agent/tools/{name}";
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolMetadata {
pub name: String,
pub description: String,
pub input: serde_json::Value,
pub output: serde_json::Value,
pub scopes: Vec<String>,
pub effect: ToolEffect,
pub idempotent: bool,
pub enqueues_job: Option<String>,
pub timeout: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ToolEffect {
Read,
Write,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentManifest {
pub name: String,
pub version: String,
pub description: String,
pub storage_mode: String,
pub tools: Vec<ToolMetadata>,
pub jobs: Vec<JobMetadata>,
pub endpoints: Vec<EndpointMetadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct JobMetadata {
pub name: String,
pub description: String,
pub payload: serde_json::Value,
pub queue: String,
pub max_retries: u32,
pub timeout: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EndpointMetadata {
pub path: String,
pub method: String,
pub description: String,
pub authenticated: bool,
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_tool() -> ToolMetadata {
ToolMetadata {
name: "get_user".to_string(),
description: "Get a user by ID".to_string(),
input: serde_json::json!({"type": "object", "properties": {"id": {"type": "string"}}}),
output: serde_json::json!({"type": "object"}),
scopes: vec!["read:users".to_string()],
effect: ToolEffect::Read,
idempotent: true,
enqueues_job: None,
timeout: Some(30),
}
}
#[test]
fn test_tool_metadata_serialization() {
let tool = sample_tool();
let json = serde_json::to_value(&tool).unwrap();
assert_eq!(json["name"], "get_user");
assert_eq!(json["effect"], "Read");
assert!(json["idempotent"].as_bool().unwrap());
}
#[test]
fn test_tool_metadata_deserialization() {
let json = serde_json::json!({
"name": "create_order",
"description": "Create an order",
"input": {"type": "object"},
"output": {"type": "object"},
"scopes": ["write:orders"],
"effect": "Write",
"idempotent": false,
"enqueues_job": "order_processing",
"timeout": 60
});
let tool: ToolMetadata = serde_json::from_value(json).unwrap();
assert_eq!(tool.name, "create_order");
assert!(matches!(tool.effect, ToolEffect::Write));
assert_eq!(tool.enqueues_job, Some("order_processing".to_string()));
}
#[test]
fn test_agent_manifest_serialization() {
let manifest = AgentManifest {
name: "test-app".to_string(),
version: "1.0.0".to_string(),
description: "Test".to_string(),
storage_mode: "memory".to_string(),
tools: vec![sample_tool()],
jobs: vec![],
endpoints: vec![],
};
let json = serde_json::to_value(&manifest).unwrap();
assert_eq!(json["name"], "test-app");
assert!(json["tools"].as_array().unwrap().len() == 1);
}
#[test]
fn test_job_metadata() {
let job = JobMetadata {
name: "send_email".to_string(),
description: "Send an email".to_string(),
payload: serde_json::json!({"to": "test@example.com"}),
queue: "email_queue".to_string(),
max_retries: 3,
timeout: 120,
};
let json = serde_json::to_value(&job).unwrap();
assert_eq!(json["queue"], "email_queue");
assert_eq!(json["max_retries"], 3);
}
#[test]
fn test_endpoint_metadata() {
let endpoint = EndpointMetadata {
path: "/api/users".to_string(),
method: "GET".to_string(),
description: "List users".to_string(),
authenticated: true,
};
let json = serde_json::to_value(&endpoint).unwrap();
assert_eq!(json["path"], "/api/users");
assert!(json["authenticated"].as_bool().unwrap());
}
}