gannet-mcp

A Model Context Protocol (MCP) server implementation in Rust for web searching and webpage fetching.
Features
Architecture
gannet-mcp/
āāā Cargo.toml # Project dependencies
āāā src/
ā āāā main.rs # Binary entry point
ā āāā lib.rs # Library root
ā āāā service.rs # `service` subcommand (OS service install/run/status)
ā āāā mcp_config.rs # `mcp-config` subcommand (client JSON snippet)
ā āāā server.rs # MCP server (rmcp 3.x #[tool]/#[tool_router])
ā āāā config.rs # Configuration management
ā āāā error.rs # Error types
ā āāā models/ # Data models
ā ā āāā mod.rs
ā ā āāā search.rs # Search request/response models
ā ā āāā content.rs # Webpage content models
ā ā āāā config.rs # Extended config structures
ā āāā services/ # External service integrations
ā ā āāā mod.rs
ā ā āāā search_service.rs # Search providers (DDG, BrightData, Serper, SearXNG)
ā ā āāā fetch_service.rs # Webpage fetching service
ā āāā handlers/ # MCP protocol handlers (active)
ā āāā mod.rs
ā āāā search_handler.rs # Search tool handler
ā āāā fetch_handler.rs # Fetch tool handler
āāā tests/
ā āāā integration_test.rs # STDIO JSON-RPC integration tests
ā āāā fetch_service_test.rs # Mocked fetch tests (mockito)
ā āāā search_provider_test.rs # Search provider + config tests
āāā .env.example # Environment variables template
āāā mcp.json # IDE MCP registration (searxng)
āāā .cursor/mcp.json # Cursor IDE MCP registration
āāā free_search_providers.md # Comparison of free search providers
āāā search_provider.md # Guide to getting free API keys
āāā README.md # This file
Installation
Prerequisites
- Rust 1.70+ (with Cargo)
- OpenSSL (for TLS support)
Build from Source
# Clone the repository
git clone https://github.com/reinartz/gannet-mcp.git
cd gannet-mcp
# Build release version
cargo build --release
# The binary will be at target/release/gannet-mcp
Installation with Cargo
cargo install --path .
Configuration
The server can be configured via:
- Configuration file (YAML or JSON via the
config crate)
- Environment variables (prefixed with
MCP__)
- Command-line arguments
Configuration File
Create a config.yaml:
server:
use_stdio: true
host: "127.0.0.1"
port: 8080
search:
provider: "duckduckgo"
default_limit: 10
max_limit: 100
timeout_secs: 10
rate_limit:
enabled: true
requests_per_minute: 60
max_concurrent: 10
logging:
level: "info"
format: "pretty"
Environment Variables
See .env.example for all available options:
cp .env.example .env
# Edit .env with your configuration
Command Line
gannet-mcp --help
Usage
STDIO Mode (Recommended for MCP)
# Run with default settings
gannet-mcp
# With custom log level
gannet-mcp --log-level debug
# Using config file
gannet-mcp --config config.yaml
HTTP Mode
# Serve MCP over Streamable HTTP (default binds localhost)
gannet-mcp --http
# Custom endpoint path
gannet-mcp --http --mcp-path /api/mcp
OS Service (HTTP daemon)
sudo gannet-mcp service install # install + enable + start (systemd/launchd/SCM)
gannet-mcp service status # no root required
sudo gannet-mcp service restart # start | stop | uninstall likewise
install points the OS supervisor at the hidden gannet-mcp service run
entry point, which always serves HTTP (never STDIO).
MCP Client Config
gannet-mcp mcp-config --client claude --stdio --print # emit JSON snippet
Supported clients: claude | cursor | zed | opencode | continue | copilot;
--stdio (default) or --http; --print (default) or --write.
> Security note: HTTP mode has no authentication in v0.2.0 (bearer-token
> auth is a planned follow-up). Bind localhost (the default) and expose it
> via a reverse proxy with TLS if remote access is needed. Avoid binding
> 0.0.0.0 on an untrusted network.
The server provides two main tools:
web_search
Search the web for information.
{
"name": "web_search",
"arguments": {
"query": "rust programming language",
"limit": 10,
"language": "en"
}
}
Parameters:
query (required): The search query string
limit (optional): Maximum results (default: 10, max: 100)
offset (optional): Pagination offset
language (optional): Language code (en, es, fr, etc.)
safe_search (optional): off, moderate, strict
region (optional): Region code for localized results
web_fetch
Fetch and parse a webpage.
{
"name": "web_fetch",
"arguments": {
"url": "https://example.com",
"extract_links": true,
"extract_images": false
}
}
Parameters:
url (required): URL to fetch
include_raw_html (optional): Include raw HTML in response
extract_links (optional): Extract links (default: true)
extract_images (optional): Extract images (default: false)
timeout_secs (optional): Request timeout (default: 30)
max_content_size (optional): Max content size in bytes (default: 10MB)
Search Providers
DuckDuckGo (Default)
No configuration required. Uses the ddgs crate (not HTML scraping).
Serper API
- Get an API key from Serper (2,500 free queries, no credit card)
- Configure:
export MCP__SEARCH__PROVIDER=serper
export MCP__SEARCH__API_KEY=your_api_key
SearXNG (Self-Hosted)
- Run SearXNG via Docker:
docker run -d -p 8081:8080 searxng/searxng
- Configure:
export MCP__SEARCH__PROVIDER=searxng
export MCP__SEARCH__BASE_URL=http://127.0.0.1:8081
BrightData
Generic Bright Data SERP API (works with any Bright Data SERP zone).
export MCP__SEARCH__PROVIDER=brightdata
export MCP__SEARCH__API_KEY=your_brightdata_api_key
export MCP__SEARCH__SEARCH_ENGINE_ID=serp_api1
Documentation
Development
Project Structure
The codebase follows Rust best practices:
- Models: Pure data structures with serialization
- Services: Business logic and external integrations (implement
SearchProvider trait)
- Handlers: MCP protocol request handling
- Server: Core MCP server implementation using
rmcp v3.x macros (#[tool], #[tool_router])
Running Tests
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_name
# Format code
cargo fmt
# Check formatting
cargo fmt -- --check
Linting
# Run clippy
cargo clippy -- -D warnings
Error Handling
The server uses a custom error type ServerError with the following variants:
Network: HTTP/network errors
UrlParse: Invalid URL format
JsonSerialize: JSON serialization errors
Config: Configuration errors
SearchApi: Search provider API errors
RateLimitExceeded: Rate limit exceeded
InvalidRequest: Invalid request parameters
NotFound: Resource not found
Timeout: Request timeout
McpProtocol: MCP protocol errors
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests and linting
- Submit a pull request
License
Dual-licensed under MIT OR Apache-2.0 ā see LICENSE-MIT and LICENSE-APACHE files for details.
Acknowledgments
Built with:
- Tokio - Async runtime
- Reqwest - HTTP client
- Scraper - HTML parsing
- rmcp - MCP protocol implementation (v3.x)
- ddgs - DuckDuckGo search client
- clap - CLI argument parsing
- config - Configuration management