fetchkit
AI-friendly web content fetching tool designed for LLM consumption. Rust library with CLI, MCP server, and Python bindings.
Features
- HTTP fetching - GET and HEAD methods with streaming support
- Pluggable fetchers - URL-aware dispatch to specialized handlers for repos, docs, feeds, videos, papers, and more
- HTML-to-Markdown - Built-in conversion optimized for LLMs, with fetched relative links/images resolved to absolute URLs
- Agent content focus - Optional low-noise extraction mode for AI agents
- Crawl discovery - Optional bounded same-origin page discovery for AI agents
- HTML-to-Text - Plain text extraction with clean formatting
- Binary detection - Returns metadata only for images, PDFs, etc.
- Timeout handling - 1s first-byte, 30s body with partial content on timeout
- Safety limits - 10 MB default decompressed body cap with truncation
- URL filtering - URL-aware allow/block lists for controlled access
- SSRF protection - Resolve-then-check blocks private IPs by default
- MCP server - Model Context Protocol support for AI tool integration
Built-in Fetchers
Fetchkit routes each request through an ordered fetcher registry. Specialized fetchers match first; the default fetcher handles everything else.
GitHubCodeFetcher- GitHub source file URLs (/blob/...)GitHubIssueFetcher- GitHub issue and pull request URLsGitHubRepoFetcher- GitHub repository home pagesTwitterFetcher- X/Twitter status URLsStackOverflowFetcher- Stack Overflow and Stack Exchange question URLsPackageRegistryFetcher- PyPI, crates.io, and npm package pagesWikipediaFetcher- Wikipedia article URLsYouTubeFetcher- YouTube watch andyoutu.beURLsArXivFetcher- arXiv abstract and PDF URLsHackerNewsFetcher- Hacker News item threadsRSSFeedFetcher- RSS and Atom feed URLsDocsSiteFetcher- docs sites withllms.txt/llms-full.txtsupportDefaultFetcher- all remaining HTTP/HTTPS URLs with HTML conversion, streaming, timeout handling, and binary detection
Installation
From crates.io (recommended)
From Git
From Source
CLI Usage
# Fetch URL (outputs markdown with frontmatter)
# Output as JSON instead
# Custom user agent
# Hardened outbound policy for cluster/data-plane use
# Discover a small same-origin page map for an agent
# Optional JS/DOM rendering for simple SPAs/docs (requires render-rakers feature)
# Show full documentation
Default output is markdown with YAML frontmatter:
url: https://example.com
status_code: 200
source_content_type: text/html; charset=UTF-8
source_size: 1256
quality_score: 1.00
extraction_method: "full"
This domain is for use in illustrative examples in documents...
JSON output (-o json):
MCP Server
Run as a Model Context Protocol server:
# Hardened profile for cluster/data-plane use
Exposes fetchkit tool over JSON-RPC 2.0 stdio transport. Returns markdown with frontmatter (same format as CLI). Compatible with Claude Desktop and other MCP clients.
Library Usage
Add to Cargo.toml:
[]
= "0.2"
Optional rendered fetching:
[]
= { = "0.2", = ["render-rakers"] }
render-rakers is not enabled by default. It is lightweight partial rendering:
inline JavaScript can update the DOM before markdown/text conversion, but it is
not a full browser engine. FetchKit blocks rakers-initiated subresource network
access in this mode; the initial page still uses FetchKit's normal URL, DNS,
proxy, timeout, and size policies.
Basic Fetch
use ;
async
With Tool Builder
use ;
let tool = new
.enable_markdown
.enable_text
.user_agent
.allow_prefix
.block_prefix
.build;
let request = new;
let response = tool.execute.await.unwrap;
Toolkit Contract Surface
use ToolBuilder;
let builder = new.enable_save_to_file;
let tool = builder.build;
assert_eq!;
assert_eq!;
let definition = builder.build_tool_definition;
let mut service = builder.build_service;
Hardened Tool Profile
use Tool;
let tool = builder
.hardened
.allow_prefix
.build;
Python Bindings
# Simple fetch
=
# With configuration
=
=
Request Fields
| Field | Type | Description |
|---|---|---|
url |
string | URL to fetch (required, http:// or https://) |
method |
enum? | GET (default) or HEAD |
as_markdown |
bool? | Convert HTML to markdown |
as_text |
bool? | Convert HTML to plain text |
save_to_file |
string? | Non-blank destination path; validated by FileSaver before fetching |
content_focus |
string? | "full"/unset returns everything; "main" strips semantic boilerplate; "readable" selects article-like content; "agent" selects the best low-noise strategy for AI agents |
crawl |
bool? | Fetch the seed URL, then discover and fetch bounded same-origin pages |
max_pages |
int? | Maximum crawl pages, including the seed; default 5, max 20 |
if_none_match |
string? | ETag for conditional If-None-Match |
if_modified_since |
string? | Timestamp for conditional If-Modified-Since |
render |
string? | "rakers" to opt into rendered fetch when enabled |
Response Fields
| Field | Type | Description |
|---|---|---|
url |
string | Fetched URL |
status_code |
int | HTTP status code |
content_type |
string? | Content-Type header |
size |
int? | Content size in bytes |
last_modified |
string? | Last-Modified header |
etag |
string? | ETag header (use for next conditional request) |
filename |
string? | From Content-Disposition |
format |
string? | "markdown", "text", "raw", or a fetcher-specific format |
content |
string? | Page content |
truncated |
bool? | True if content was cut off |
method |
string? | "HEAD" for HEAD requests |
error |
string? | Error message if failed |
saved_path |
string? | Filesystem path when save_to_file succeeded |
bytes_written |
int? | Bytes saved to file |
metadata |
object? | Structured PageMetadata (title, description, links, headings, extraction method, …) |
quality |
object? | Agent-facing PageQuality (score, warnings, link density, suggested next action) |
crawl |
object? | Bounded crawl discovery result with visited page summaries |
word_count |
int? | Word count of returned content |
redirect_chain |
string[] | URLs visited during redirects (empty if none) |
is_paywall |
bool? | Heuristic paywall signal (soft, not guaranteed) |
rendered_by |
string? | Rendering backend used before conversion, e.g. "rakers" |
Error Handling
Errors are returned in the error field:
InvalidUrl- Malformed URLUrlBlocked- URL blocked by filterNetworkError- Connection failedTimeout- Request timed outHttpError- 4xx/5xx responseContentError- Failed to read bodyBinaryContent- Binary content not supported
Security
Fetchkit blocks connections to private/reserved IP ranges by default, preventing SSRF attacks when used in server-side or AI agent contexts.
Blocked by default: loopback, private networks (10.x, 172.16-31.x, 192.168.x), link-local (169.254.x including cloud metadata), IPv6 equivalents, multicast, and other reserved ranges.
// Default: private IPs blocked (safe for production)
let tool = default;
// Explicit opt-out for local development only
let tool = builder
.block_private_ips
.build;
DNS pinning prevents DNS rebinding attacks. IPv6-mapped IPv4 addresses are canonicalized before validation.
Redirects are followed manually in the default fetcher so each hop is revalidated against scheme and DNS policy. Allow/block prefixes are matched against parsed URLs rather than raw strings, which prevents lookalike host overmatches such as allowed.example.com.evil.test.
Proxy environment variables are ignored by default. Use the hardened profile for cluster-facing deployments and opt in with ToolBuilder::respect_proxy_env(true) only when it is part of an intentional egress design.
See specs/threat-model.md for the full threat model.
See docs/hardening.md for deployment guidance.
Configuration
Timeouts And Limits
- First-byte: 1 second (connect + initial response)
- Body: 30 seconds total
- Body size: 10 MB decompressed content by default
Partial content is returned on body timeout or body-size limit with truncated: true.
Binary Content
Automatically detected and returns metadata only for:
- Images, audio, video, fonts
- PDFs, archives (zip, tar, rar, 7z)
- Office documents
HTML Conversion
HTML is automatically converted to markdown:
- Headers:
h1-h6→#to###### - Lists: Proper nesting with 2-space indent
- Code: Fenced blocks and inline backticks
- Links:
[text](url)format - Strips: scripts, styles, iframes, SVGs
License
MIT