Documentation Index
Fetch the complete documentation index at: https://mintlify.com/dallay/corvus/llms.txt
Use this file to discover all available pages before exploring further.
Example: HTTP GET Tool
A complete example of building a tool that fetches URLs and returns status codes. Source:examples/custom_tool.rs
Full Implementation
//! Example: Implementing a custom Tool for Corvus
use anyhow::Result;
use async_trait::async_trait;
use serde_json::{json, Value};
/// Mirrors src/tools/traits.rs
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ToolResult {
pub success: bool,
pub output: String,
pub error: Option<String>,
}
#[async_trait]
pub trait Tool: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn parameters_schema(&self) -> Value;
async fn execute(&self, args: Value) -> Result<ToolResult>;
}
/// Example: A tool that fetches a URL and returns the status code
pub struct HttpGetTool;
#[async_trait]
impl Tool for HttpGetTool {
fn name(&self) -> &str {
"http_get"
}
fn description(&self) -> &str {
"Fetch a URL and return the HTTP status code and content length"
}
fn parameters_schema(&self) -> Value {
json!({
"type": "object",
"properties": {
"url": { "type": "string", "description": "URL to fetch" }
},
"required": ["url"]
})
}
async fn execute(&self, args: Value) -> Result<ToolResult> {
let url = args["url"]
.as_str()
.ok_or_else(|| anyhow::anyhow!("Missing 'url' parameter"))?;
match reqwest::get(url).await {
Ok(resp) => {
let status = resp.status().as_u16();
let len = resp.content_length().unwrap_or(0);
Ok(ToolResult {
success: status < 400,
output: format!("HTTP {status} — {len} bytes"),
error: None,
})
}
Err(e) => Ok(ToolResult {
success: false,
output: String::new(),
error: Some(format!("Request failed: {e}")),
}),
}
}
}
Integration Steps
1. Add to Corvus
Copy implementation tosrc/tools/http_get.rs
2. Register Tool
Add tosrc/tools/mod.rs:
pub mod http_get;
pub fn default_tools(
security: Arc<SecurityPolicy>,
memory: Arc<dyn Memory>,
runtime: Arc<dyn RuntimeAdapter>,
config: &Config,
) -> Vec<Arc<dyn Tool>> {
vec![
// ... existing tools
Arc::new(http_get::HttpGetTool),
]
}
3. Test
Add tests insrc/tools/http_get.rs:
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_http_get_success() {
let tool = HttpGetTool;
let result = tool.execute(json!({
"url": "https://httpbin.org/status/200"
})).await.unwrap();
assert!(result.success);
assert!(result.output.contains("HTTP 200"));
}
#[tokio::test]
async fn test_http_get_not_found() {
let tool = HttpGetTool;
let result = tool.execute(json!({
"url": "https://httpbin.org/status/404"
})).await.unwrap();
assert!(!result.success);
assert!(result.output.contains("HTTP 404"));
}
}
cargo test tools::http_get
4. Use
The agent can now fetch URLs:corvus agent -m "Check if https://docs.rs is online"
Let me check...
[Tool: http_get] {"url": "https://docs.rs"}
✔ HTTP 200 — 12345 bytes
Yes, https://docs.rs is online and responding with HTTP 200.
Enhancements
Add Headers Support
fn parameters_schema(&self) -> Value {
json!({
"type": "object",
"properties": {
"url": { "type": "string" },
"headers": {
"type": "object",
"description": "Custom headers (key-value pairs)"
}
},
"required": ["url"]
})
}
async fn execute(&self, args: Value) -> Result<ToolResult> {
let url = args["url"].as_str()?;
let mut req = reqwest::Client::new().get(url);
if let Some(headers) = args["headers"].as_object() {
for (key, value) in headers {
if let Some(val) = value.as_str() {
req = req.header(key, val);
}
}
}
// Execute request...
}
Add Timeout
use std::time::Duration;
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()?;
let resp = client.get(url).send().await?;
Return JSON Body
match reqwest::get(url).await {
Ok(resp) => {
let status = resp.status().as_u16();
let body = resp.text().await?;
let output = serde_json::json!({
"status": status,
"body": body,
"success": status < 400
});
Ok(ToolResult {
success: status < 400,
output: output.to_string(),
error: None,
})
}
// ...
}