# 🔍 Daedra
[](https://crates.io/crates/daedra)
[](https://docs.rs/daedra)
[](https://github.com/dirmacs/daedra/actions/workflows/ci.yml)
[](https://opensource.org/licenses/MIT)
**Daedra** is a high-performance web search and research [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server written in Rust. It provides web search and page fetching capabilities that can be used with AI assistants like Claude.
## Features
- 🔎 **Web Search**: Search the web using DuckDuckGo with customizable options
- 📄 **Page Fetching**: Extract and convert web page content to Markdown
- 🚀 **High Performance**: Built in Rust with async I/O and connection pooling
- 💾 **Caching**: Built-in response caching for improved performance
- 🔌 **Dual Transport**: Support for both STDIO and HTTP (SSE) transports
- 📦 **Library & CLI**: Use as a Rust library or standalone command-line tool
## Installation
### From crates.io
```bash
cargo install daedra
```
### From source
```bash
git clone https://github.com/dirmacs/daedra.git
cd daedra
cargo install --path .
```
### Using Cargo
Add to your `Cargo.toml`:
```toml
[dependencies]
daedra = "0.1"
```
## Quick Start
### As an MCP Server
#### STDIO Transport (for Claude Desktop, Cursor, etc.)
Add to your Claude Desktop configuration (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"daedra": {
"command": "daedra",
"args": ["serve", "--transport", "stdio", "--quiet"]
}
}
}
```
> **Note:** The `--quiet` flag is recommended for MCP clients to suppress log output. Without it, logs are written to stderr which some clients may display.
```
#### SSE Transport (HTTP)
```bash
daedra serve --transport sse --port 3000 --host 127.0.0.1
```
### As a CLI Tool
#### Search the web
```bash
# Basic search
daedra search "rust programming"
# With options
daedra search "rust async" --num-results 20 --region us-en --safe-search moderate
# Output as JSON
daedra search "rust web frameworks" --format json
```
#### Fetch a webpage
```bash
# Fetch and extract content
daedra fetch https://rust-lang.org
# Fetch with a specific selector
daedra fetch https://example.com --selector "article.main"
# Output as JSON
daedra fetch https://example.com --format json
```
#### Server information
```bash
daedra info
```
#### Configuration check
```bash
daedra check
```
### As a Rust Library
```rust
use daedra::{DaedraServer, ServerConfig, TransportType};
use daedra::tools::{search, fetch};
use daedra::types::{SearchArgs, SearchOptions, VisitPageArgs};
// Start an MCP server
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = ServerConfig::default();
let server = DaedraServer::new(config)?;
server.run(TransportType::Stdio).await?;
Ok(())
}
// Or use tools directly
async fn search_example() -> anyhow::Result<()> {
let args = SearchArgs {
query: "rust programming".to_string(),
options: Some(SearchOptions {
num_results: 10,
region: "wt-wt".to_string(),
..Default::default()
}),
};
let results = search::perform_search(&args).await?;
println!("Found {} results", results.data.len());
for result in results.data {
println!("- {} ({})", result.title, result.url);
}
Ok(())
}
async fn fetch_example() -> anyhow::Result<()> {
let args = VisitPageArgs {
url: "https://rust-lang.org".to_string(),
selector: None,
include_images: false,
};
let content = fetch::fetch_page(&args).await?;
println!("Title: {}", content.title);
println!("Word count: {}", content.word_count);
Ok(())
}
```
## MCP Tools
### `search_duckduckgo`
Search the web using DuckDuckGo.
**Input Schema:**
```json
{
"query": "search terms",
"options": {
"region": "wt-wt",
"safe_search": "MODERATE",
"num_results": 10,
"time_range": "w"
}
}
```
**Options:**
- `region`: Search region (e.g., `us-en`, `wt-wt` for worldwide)
- `safe_search`: `OFF`, `MODERATE`, or `STRICT`
- `num_results`: Number of results (1-50)
- `time_range`: Time filter (`d`=day, `w`=week, `m`=month, `y`=year)
### `visit_page`
Fetch and extract content from a web page.
**Input Schema:**
```json
{
"url": "https://example.com",
"selector": "article.main",
"include_images": false
}
```
**Options:**
- `url`: URL to fetch (required)
- `selector`: CSS selector for specific content (optional)
- `include_images`: Include image references (default: false)
## Configuration
### Environment Variables
- `RUST_LOG`: Set logging level (`debug`, `info`, `warn`, `error`)
### CLI Options
```
daedra serve [OPTIONS]
Options:
-t, --transport <TRANSPORT> Transport type [default: stdio] [possible values: stdio, sse]
-p, --port <PORT> Port for SSE transport [default: 3000]
--host <HOST> Host to bind to [default: 127.0.0.1]
--no-cache Disable result caching
--cache-ttl <SECONDS> Cache TTL in seconds [default: 300]
-v, --verbose Enable verbose output (debug logging)
-q, --quiet Disable all logging output
-f, --format <FORMAT> Output format [default: pretty] [possible values: pretty, json, json-compact]
--no-color Disable colored output
```
> **Note:** For stdio transport, logs are automatically routed to stderr to prevent corruption of the JSON-RPC stream. Use `--quiet` to disable logging entirely.
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ CLI Binary │
│ (clap argument parsing, colored output, TUI) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Library (daedra) │
├─────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Server │ │ Tools │ │ Cache │ │
│ │ (rmcp MCP) │ │ (search/ │ │ (moka) │ │
│ │ │ │ fetch) │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────┤
```
## Performance
Daedra is designed for high performance:
- **Async I/O**: Built on Tokio for efficient async operations
- **Connection Pooling**: HTTP connections are pooled and reused
- **Caching**: Results are cached to avoid redundant requests
- **Concurrent Processing**: Parallel search execution support
- **Efficient Parsing**: Fast HTML parsing with the `scraper` crate
## Development
### Prerequisites
- Rust 1.91 or later
- Cargo
### Building
```bash
# Debug build
cargo build
# Release build
cargo build --release
```
### Testing
```bash
# Run all tests
cargo test
# Run unit tests only
cargo test --lib
# Run integration tests (requires network)
cargo test -- integration
# Run with logging
RUST_LOG=debug cargo test
```
### Benchmarks
```bash
cargo bench
```
### Documentation
```bash
# Generate and open documentation
cargo doc --open
```
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
### Code Style
This project uses:
- `rustfmt` for formatting
- `clippy` for linting
Run before committing:
```bash
cargo fmt
cargo clippy -- -D warnings
```
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## Related Projects
- [rmcp](https://github.com/modelcontextprotocol/rust-sdk) - Rust MCP SDK
- [mcp-duckduckresearch](https://github.com/bkataru-workshop/mcp-duckduckresearch) - TypeScript inspiration
- [DIRMACS](https://github.com/dirmacs) - Parent organization
---
Made with ❤️ by [DIRMACS Global Services](https://dirmacs.com)