Bintu Web Reader

io.github.l20941485-blip由 GitHub 帳號 l20941485-blip 發布v1.0.2更新於 Oct 27, 2026

Fetch public web pages as clean, token-budgeted text for AI agents.

已驗證Streamable HTTP可網頁執行網頁搜尋與抓取知識與記憶

概覽

AI 產生的概覽

遠端 MCP 伺服器,擷取公開網頁並回傳乾淨、依 token 預算裁剪的文字,供 AI 助理閱讀。

可以做什麼
  • 擷取公開的 HTTP 或 HTTPS 頁面,回傳可讀的 Markdown 文字,並附上約略 token 數與截斷標記
  • 預設移除導覽列、頁首、頁尾、廣告與隱藏元素,保留正文或主內容區,並將連結輸出為絕對網址
  • 把長文章或文件切成有序、依 token 預算控制大小的分塊,方便檢索流程使用
  • 回傳快取命中、限制因素等擷取中繼資料,並在頁面可讀內容過少時提供低產出提示
適用情境
當助理需要閱讀公開網頁、長文章或文件內容,又不想把原始 HTML 塞進上下文視窗時使用;也適合需要把網頁文字切成模型可處理分塊的情境。
執行需求
透過 Streamable HTTP 連線的遠端 MCP 端點,不需本機安裝。必須驗證:以 Authorization 承載權杖方式送出 MCPize 訂閱金鑰,提供免費方案。上游服務另支援以 API 金鑰標頭直接呼叫 REST 介面。
安裝前請注意
這是付費閘道服務:需要訂閱金鑰,配額由閘道計費,使用可能產生費用。金鑰屬於機密,不應放入網址或提交到程式碼庫。請求會把目標網頁位址送給第三方服務,該服務會快取擷取結果;僅支援公開頁面,私有網路位址會被阻擋。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Bintu Web Reader,將其新增到工作區。
  2. 連接 所需的 憑證。它們會被私密儲存,絕不會在公開頁面上展示。
  3. 為需要使用其工具的對話啟用該服務。

Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。

交給代理程式安裝

把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。

閱讀 https://sourceweft.com/mcp/io.github.l20941485-blip%2Fbintu/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。

檢視代理程式所依據的安裝指南

其他 MCP 客戶端

把它新增到你客戶端的 mcpServers 設定中。 客戶端會在首次連接時完成 OAuth 登入。

{
  "mcpServers": {
    "bintu-web-reader": {
      "type": "http",
      "url": "https://bintu-web-reader.mcpize.run/mcp"
    }
  }
}

README

Bintu Data Extraction API

Quickstart

1. Authenticate

Send your service key in the X-API-Key header. Do not put API keys in URLs.

powershell
$BaseUrl = "https://your-bintu-api.onrender.com"$ApiKey  = $env:BINTU_API_KEY

2. Extract readable text

powershell
$url = "https://example.com"
$response = Invoke-RestMethod `  -Method Get `  -Uri "$BaseUrl/api/v1/scrape/text?url=$([uri]::EscapeDataString($url))" `  -Headers @{ "X-API-Key" = $ApiKey }
$response.text$response.approx_tokens$response.truncated

The response includes text, an approximate token count, and truncated. If a page returns very little readable text, the response may include low_yield: true and a short yield_hint, such as a note that the page appears to require JavaScript.

3. Use MCP

MCP discovery is available without a key. Tool execution requires either the scoped MCP_API_KEY or the master API_KEY, sent as X-API-Key or Authorization: Bearer.

powershell
$body = @{  jsonrpc = "2.0"  id = 1  method = "tools/call"  params = @{    name = "bintu_scrape_text"    arguments = @{      url = "https://example.com"      max_tokens = 2000    }  }} | ConvertTo-Json -Depth 10
Invoke-RestMethod `  -Method Post `  -Uri "$BaseUrl/mcp" `  -Headers @{    "X-API-Key" = $ApiKey    "Accept" = "application/json, text/event-stream"  } `  -ContentType "application/json" `  -Body $body

Common responses

SituationWhat to check
401 invalid_api_keySend a valid REST X-API-Key; an MCP-only key does not unlock REST routes.
truncated: trueRaise max_tokens, or request the next chunk using the continuation endpoint.
low_yield: trueTry a print, AMP, RSS, or server-rendered version of the page.

Bintu is a FastAPI-based API for extracting readable text from public HTML pages. It limits upstream responses to 1 MiB, accepts HTML/XHTML only, pins outbound connections to validated public IPs, and applies a 60-request per-minute process-local burst limit.

Live API

The service is live on Render and ready to test:

Health check

bash
curl https://bintu-api.onrender.com/healthz

Example response:

json
{  "status": "ok"}

Scrape text

For direct access to the deployed service, set your API key in PowerShell. The secure prompt avoids echoing the key as you type:

powershell
$env:BINTU_BASE_URL = "https://bintu-api.onrender.com"$secure = Read-Host "Paste your Render API_KEY" -AsSecureString$env:BINTU_API_KEY = [System.Net.NetworkCredential]::new("", $secure).PasswordRemove-Variable secure

Send an authenticated scrape request:

powershell
$target = "https://www.iana.org/domains/reserved"$encodedUrl = [uri]::EscapeDataString($target)$headers = @{ "X-API-Key" = $env:BINTU_API_KEY }
try {    $result = Invoke-RestMethod `        -Uri "$env:BINTU_BASE_URL/api/v1/scrape/text?url=$encodedUrl" `        -Headers $headers    $result | ConvertTo-Json -Depth 5}catch {    $response = $_.Exception.Response    if ($response) {        $statusCode = [int]$response.StatusCode        $retryAfter = $response.Headers["Retry-After"]        Write-Host "HTTP $statusCode; Retry-After: $retryAfter"        $reader = [System.IO.StreamReader]::new($response.GetResponseStream())        try { $reader.ReadToEnd() } finally { $reader.Dispose() }    }    else {        throw    }}

Successful response:

json
{  "url": "https://www.iana.org/domains/reserved",  "text": "# IANA-managed Reserved Domains\n## Example domains\n...",  "truncated": false}

truncated is true if the extracted text exceeded the configured character limit. The API returns the requested URL in url.

For curl.exe in PowerShell, the same request can be made with:

powershell
curl.exe --get "$env:BINTU_BASE_URL/api/v1/scrape/text" `  --data-urlencode "url=https://www.iana.org/domains/reserved" `  --header "X-API-Key: $env:BINTU_API_KEY" `  --header "Accept: application/json"

The Render API_KEY is available in the service's Environment settings. Keep it private, do not commit it, and do not put it in a URL. The examples above keep it in the current PowerShell process environment. If no API key or proxy secret is configured (as in the default local development setup), authentication is not required. Interactive API docs are available at /docs — click Authorize (top right) and paste the key into ApiKeyAuth, or the Supabase session token into BearerAuth for the mobile endpoint, then use Try it out on any locked route.

Scrape chunks

Use POST /api/v1/scrape/chunks when the extracted page needs to be divided into model-friendly, approximately token-budgeted pieces. This is useful for long articles, documentation, and retrieval workflows.

Set the API key as shown in Scrape text, then send:

powershell
$target = "https://www.iana.org/domains/reserved"
$body = @{    url = $target    include_nav = $false    include_header = $false    include_footer = $false    render_js = $false} | ConvertTo-Json
$result = Invoke-RestMethod `    -Method Post `    -Uri "$env:BINTU_BASE_URL/api/v1/scrape/chunks?max_tokens=1000" `    -Headers @{ "X-API-Key" = $env:BINTU_API_KEY } `    -ContentType "application/json" `    -Body $body
$result | ConvertTo-Json -Depth 5

The response contains ordered chunks:

json
{  "url": "https://www.iana.org/domains/reserved",  "mode": "chunk",  "max_tokens": 1000,  "split": true,  "chunk_count": 3,  "chunks": [    {      "index": 0,      "text": "# IANA-managed Reserved Domains\n...",      "tokens": 987    }  ]}

max_tokens controls the approximate size of each chunk, not the total amount of source retained. It must be between 100 and 50,000; the default is 6,000. Chunk boundaries preserve words and URLs, and fenced Markdown code blocks are kept intact when they fit within the requested budget. An oversized code block is split only within its body, with valid opening and closing fences restored to every resulting chunk.

Like the text endpoint, chunk mode retains up to 200,000 extracted characters. If that ceiling is reached, the response is truncated before chunking and returns truncated: true with limiting_factor: "char_cap".

Response fields

FieldMeaning
urlRequested or resolved source URL.
textReadable extracted text or Markdown.
approx_tokensApproximate token count of text; calculated as ceil(characters / 4).
tokensDeprecated alias for approx_tokens; retained for backward compatibility.
tokens_saved_estimateApproximate tokens saved relative to the fetched HTML; an estimate, not a billing figure.
truncatedtrue when a character or token limit shortened the returned content.
limiting_factorWhich limit applied: char_cap, token_budget, or none.
cache_hittrue when the response came from either cache layer.
cache_layernone, memory, or persistent.
low_yieldtrue when a large page produced very little readable text.
yield_hintShort explanation when low_yield is true.
provenanceOptional request, fetch, and cache metadata.

Common errors

Errors use a JSON detail object with code, message, and, when applicable, hint. For example, a missing or invalid key returns HTTP 401:

json
{  "detail": {    "code": "invalid_api_key",    "message": "A valid API key is required."  }}

An invalid or unsafe URL returns HTTP 400:

json
{  "detail": {    "code": "invalid_or_unsafe_url",    "message": "URL must be an absolute HTTP or HTTPS URL",    "hint": "URL must start with http:// or https:// and be publicly accessible"  }}
HTTP statusError code(s)What to do
400invalid_or_unsafe_url, render_js_disabled, invalid_token_budgetUse a well-formed public HTTP or HTTPS URL. Private-network destinations are blocked. render_js=true is disabled unless the service enables it — retry with render_js=false. For chunk requests, max_tokens must be between 100 and 50,000.
401invalid_api_keySend the current X-API-Key configured in Render.
500auth_misconfigurationREQUIRE_AUTH=true is set (Render production default) but API_KEY and RAPIDAPI_PROXY_SECRET are both empty. Restore the secrets in the Render dashboard, then redeploy.
413payload_too_largeThe HTML exceeds 1 MiB after decompression.
415unsupported_media_typeThe target must return HTML or XHTML.
429burst_rate_limit_exceededPause for the seconds specified in the Retry-After response header, then retry.
502upstream_http_error, upstream_connection_error, upstream_render_error, unsupported_content_encoding, or too_many_redirectsThe target returned an error, could not be reached or rendered, used unsupported compression, or redirected too many times.
504upstream_timeoutThe target did not respond in time.

The 429 response also includes a Retry-After header. Avoid repeatedly retrying 4xx responses; correct the request or wait as indicated.

Run Locally

Use Python 3.12. Install the application and test dependencies, then start the service:

powershell
python -m venv .venv.\.venv\Scripts\Activate.ps1python -m pip install -r requirements-dev.txtuvicorn main:app --reload

The interactive API documentation is at /docs. With no keys configured, local development is open. Never deploy an unprotected instance; the Render blueprint generates an API_KEY by default.

Docker

powershell
docker build -t bintu-api .docker run -p 8000:8000 -e API_KEY=your-key bintu-api

Or with Docker Compose:

powershell
docker-compose up

API

GET /healthz is a lightweight Render health check. It does not require authentication.

GET /version returns service configuration (no secrets exposed).

GET /metrics exposes Prometheus request counts, error counts, latency histograms, and cache hit/miss counters. Metrics use route templates rather than request URLs and do not include scraped content. Application request logs include a request ID, method, route template, status, and duration; they do not log target URLs or page text. The Render and Docker launch configurations disable Uvicorn access logs because access-log request lines can include the url query parameter. It is protected by the same credentials as the API, so send X-API-Key (or the RapidAPI proxy header) when a key is configured. X-Request-ID is returned on every response; valid caller-provided IDs are preserved, otherwise the API generates one.

GET /api/v1/scrape/text?url=https%3A%2F%2Fexample.com returns the requested URL, extracted text, and a truncated boolean. truncated is true only when extracted content exceeded the configured text limit. When available, the extractor prefers <article> or <main> content, then a role="main" or commonly named main/content container (such as id="main"), and otherwise falls back to the full document. It omits hidden elements and common controls or page furniture, and renders links as link text (absolute URL). Headings are marked with Markdown # prefixes, paragraphs and other block elements are separated by line breaks, and table cells within each row are joined with | with a separator beneath header rows. If the document title duplicates its first heading, it is omitted. Character encoding is detected from the upstream HTTP charset and HTML metadata. Direct callers must send X-API-Key when API_KEY is configured. The endpoint supports public HTTP/HTTPS destinations only and does not bypass access controls.

POST /api/v1/scrape/chunks returns readable text as approximately token-budgeted chunks. It uses the same guarded fetching, extraction, authentication, caching, and rate limiting as /api/v1/scrape/text, but divides the extracted Markdown into ordered chunks using max_tokens.

JavaScript rendering is experimental. Keep RENDER_JS_ENABLED=false: the browser path has an unresolved DNS-rebinding time-of-check-to-time-of-use risk. Do not enable it until connection targeting, redirects, and browser subresource requests are hardened and tested. Paid infrastructure does not resolve this risk; see Security Notes. When enabled, render_js=true selects headless Chromium rendering. Rendering waits for networkidle, validates browser-requested HTTP destinations against public DNS addresses, and omits images, media, and fonts to reduce memory use. Non-rendered requests retain the existing lightweight transport that pins each connection to a validated public IP. Both modes use the same extractor, text limits, persistent cache, authentication, and rate limiting.

POST /mcp is a standard MCP Streamable HTTP endpoint (stateless JSON-RPC 2.0) for Claude custom connectors and Claude Code: it answers initialize, ping, tools/list, and tools/call for the single bintu_scrape_text tool, and requires Content-Type: application/json plus the same API key as normal requests. Point a Claude custom connector at https://<your-host>/mcp with an X-API-Key request header. Notifications get 202; GET/DELETE return 405 since the server keeps no sessions or streams.

GET /mcp/tools advertises the bintu_scrape_text tool, and POST /mcp/tools/call invokes it with {"name":"bintu_scrape_text","arguments":{"url":"https://example.com","render_js":false}}. Successful responses follow the MCP tool content shape and include the same extraction envelope as both structured content and serialized JSON: structuredContent contains the canonical extraction envelope, while content[0].text contains its JSON serialization. Tool calls use the same API authentication and rate limits as normal scrape requests.

Query Parameters

ParameterTypeDefaultDescription
urlstringrequiredThe URL to scrape
render_jsboolfalseRender JavaScript using headless Chromium before extracting
include_navboolfalsePreserve <nav> elements in output
include_headerboolfalsePreserve <header> elements in output
include_footerboolfalsePreserve <footer> elements in output

Chunk Endpoint Parameters

POST /api/v1/scrape/chunks accepts the extraction fields in its JSON body and the following query parameters:

ParameterTypeDefaultDescription
max_tokensinteger6000Approximate token budget for each chunk; must be 100–50,000
skip_cacheboolfalseBypass cached extraction and fetch the page again
persistbooltrueAllow a successful extraction to be written to the persistent cache

The JSON body accepts:

FieldTypeDefaultDescription
urlstringrequiredPublic HTTP or HTTPS URL to scrape
include_navboolfalsePreserve <nav> elements
include_headerboolfalsePreserve <header> elements
include_footerboolfalsePreserve <footer> elements
render_jsboolfalseRequest JavaScript rendering; disabled unless RENDER_JS_ENABLED=true

Service Limits

  • Request timeout: 10 seconds per connection attempt.
  • Redirects: at most 5, with URL and DNS validation repeated for every destination.
  • HTML body: at most 1 MiB after decompression, read in chunks no larger than 4 KiB. Gzip-encoded HTML is supported with the same decoded-size limit.
  • Text response: at most 6,000 characters; truncated is true when the content exceeds the limit. Truncated text ends at the last available word boundary.
  • Chunk mode: retains up to 200,000 extracted characters before chunking. Each returned chunk is budgeted by max_tokens (100–50,000; default 6,000). Fenced code blocks are preserved when they fit and are split with restored fences when they do not.
  • JavaScript rendering: disabled by default (RENDER_JS_ENABLED=false); render_js=true returns 400 render_js_disabled. Keep it disabled until the browser path is hardened and tested; paid infrastructure does not resolve the documented DNS-rebinding risk. See Security Notes.
  • Local abuse guard: 60 requests per client identity per 60 seconds, in process memory only. Behind Render, a client IP is honored only from Render's proxy range (100.64.0.0/10), so the guard cannot be bypassed by spoofing X-Forwarded-For. Set REDIS_URL to share the counter across instances; the window TTL is set once per window, and if Redis is unreachable the guard falls back to process memory rather than failing requests.
  • Process-local HTML cache: 5-minute TTL, bounded to 64 entries per instance with least-recently-used eviction. CACHE_TTL_SECONDS=0 disables this local cache. When configured, the separate Supabase Markdown cache retains successful extractions for 24 hours.
  • Paid subscription quotas and billing: configured and enforced by RapidAPI, not by this service.

Configuration

Environment VariableDefaultDescription
API_KEY(empty)API key for direct access
RAPIDAPI_PROXY_SECRET(empty)Secret for RapidAPI proxy authentication
SUPABASE_URL(empty)Supabase project URL used to verify mobile user sessions
SUPABASE_ANON_KEY(empty)Supabase publishable/anon key used for mobile session verification; never use a service-role/secret key
SUPABASE_SERVICE_ROLE_KEY(empty)Private server-side key for persistent scrape caching and RapidAPI usage accounting
CACHE_TTL_SECONDS300Process-local HTML cache TTL in seconds; 0 disables that cache (Supabase's 24-hour cache is separate). Non-numeric or out-of-range values (allowed range 0-86400) fall back to 300 with a logged warning
REDIS_URL(empty)Redis URL for multi-instance rate limiting. Empty or unreachable = per-process burst guard (fail-open), each instance enforcing its own window
RENDER_JS_ENABLEDfalseJavaScript rendering kill-switch. Keep false until the browser path is hardened and tested; see Security Notes. Only exact true enables Chromium; anything else returns 400 render_js_disabled for render_js=true
BINTU_UPGRADE_URLhttps://mcpize.comCanonical subscribe URL embedded in MCP 401 -32001 upgrade_url so agents print a clickable link in-chat

Deploy To Render

  1. Push this project to a GitHub repository.
  2. In Render, create a new Blueprint and select that repository. Render reads render.yaml, installs requirements.txt and Chromium's system dependencies, then starts Uvicorn on the assigned port.
  3. Wait for /healthz to pass. Copy the generated API_KEY from the service environment for direct API access. Do not commit either secret.
  4. Test the deployed /docs page and make an authenticated scrape request before publishing.

The free instance can sleep while idle, so the first request may be slow. Free services are best for validation and early users, not latency-sensitive production or an uptime guarantee. The local burst guard resets on restarts and is not shared across instances. It is not a paid quota; use RapidAPI plan limits as the billing authority.

Storefront parity (single source of truth)

One API, three doors. Behaviour is identical; only auth + billing differ per gateway:

Direct (Render)RapidAPIMCPize
Listing nameService bintu-data-extraction-api (MCP serverInfo.name: bintu)Bintu Data Extraction API (subdomain bintu-data-extraction-api)LLM Web Scraper & Clean Text Extractor (slug/host bintu-web-reader.mcpize.run; users label their Claude connector Bintu-web-reader)
EndpointGET /api/v1/scrape/text?url=… at https://bintu-api.onrender.comSame path at https://bintu-data-extraction-api.p.rapidapi.combintu_scrape_text tool at https://bintu-web-reader.mcpize.run (upstream POST https://bintu-api.onrender.com/mcp)
AuthX-API-Key: <Render API_KEY>X-RapidAPI-Key + X-RapidAPI-Host (gateway injects X-RapidAPI-Proxy-Secret + X-RapidAPI-User)Subscriber's MCPize key (sk_…); gateway injects the upstream X-API-Key. Never send buyers to RapidAPI from the MCPize page
BillingNone (owner key)RapidAPI plans: BASIC $0/100 · PRO $9/10k · ULTRA $29/50k · MEGA $79/100k+MCPize publisher tiers (dashboard → Monetize): Free ~1k · Pro $9–19 · Team $49+ (80% via Stripe Connect)
Quota60 req / 60 s burst guard per IP60/60s per X-RapidAPI-User + plan quota60/60s global across MCPize subscribers (single upstream token) + plan quota
Cache5-min in-memory + 24 h Supabase persistentSameSame upstream cache; cold fetch ~2–6 s, cache hit ~300 ms

Token Tax positioning (MCP): bintu_scrape_text is a Token-Sanitized Context Gateway — nav/ads/footer stripped, deterministic max_tokens chunks (chunked→chunk, chunk_token_budget→max_tokens aliases), every mode returns the unified JSON envelope core fields mode, tokens, approx_tokens, html_tokens, token_method, limiting_factor, chunk_count, cache_hit, cache_layer, source_url, extracted_at, tokens_saved_estimate, truncated, split, and chunks`` in content[0].text plus X-Token-Usage header and 401 -32001 upgrade_url (https://mcpize.com, override BINTU_UPGRADE_URL). No tier caps in code — dashboards own quota. New env: BINTU_UPGRADE_URL (default https://mcpize.com).

Chunk mode limits

Chunk mode preserves up to 200,000 extracted characters, approximately 50,000 tokens under ceil(characters / 4).

max_tokens controls the approximate size of each emitted chunk, not the total amount of source retained. Sources larger than the extraction ceiling are truncated before chunking and return truncated: true with limiting_factor: "char_cap".

Fenced Markdown code blocks are treated as atomic when they fit within a chunk budget. Oversized fenced blocks are split only within the code body; each resulting chunk receives restored opening and closing fences, keeping every chunk valid, self-contained Markdown.

Legacy scrape endpoint limits: 1 MiB upstream body, HTML/XHTML only, 6,000-char text cap (truncated flag), 5 redirects, render_js=true → 400 render_js_disabled (keep RENDER_JS_ENABLED=false until browser security hardening is complete; paid infrastructure does not resolve the risk). Error codes: see Common errors.

Usage examples

Ready-to-run clients are in examples/:

Set your credentials first:

powershell
$env:BINTU_BASE_URL = "[https://bintu-api.onrender.com](https://bintu-api.onrender.com)"$env:BINTU_API_KEY = Read-Host "Paste API key"

Then run:

powershell
python .\examples\rest_python.py [https://example.com](https://example.com)node .\examples\rest_javascript.js [https://example.com](https://example.com)python .\examples\mcp_python.py [https://example.com](https://example.com)

RapidAPI Publishing

RapidAPI handles subscriber authentication and billing at its gateway. In the RapidAPI provider dashboard, point the API base URL at the Render service and configure the provider proxy secret. Set Render's RAPIDAPI_PROXY_SECRET to the same value. Requests authenticated with X-RapidAPI-Proxy-Secret are accepted; their X-RapidAPI-User identity receives an independent 60-request-per-minute bucket. Keep API_KEY for owner/direct access, and never expose either secret in client examples.

Configure subscription plans, monthly quotas, and pricing in RapidAPI. Start with a small free trial and paid tiers based on measured Render latency, failure rates, and upstream costs. The service's 60-per-minute guard is a burst limit, not a monthly quota or billing system. Marketplace placement and revenue are not guaranteed.

Only scrape content you are authorized to access. Respect the target site's terms and robots policy. This API does not circumvent logins, CAPTCHAs, paywalls, or anti-bot controls.

Connect Claude

POST /mcp implements the standard MCP Streamable HTTP transport (protocol 2025-11-25, also accepting 2025-06-18/2025-03-26), so any MCP client can call the scraper. It is stateless: no Mcp-Session-Id is issued, GET answers with an empty SSE stream (GET /sse and GET /mcp/sse are compatibility aliases for gateways that probe a dedicated SSE path), DELETE/PUT/PATCH return 405, and each JSON-RPC message is answered with a single application/json response. Requests after initialize must carry the negotiated MCP-Protocol-Version header (unknown versions get 400), and a mismatched Origin header is rejected with 403 against DNS rebinding. initialize, tools/list, and ping are public discovery methods (protocol negotiation, tool schemas, liveness — no extraction), so gateway health-checkers, marketplace audits, and Claude Web — none of which can send headers during onboarding — complete instantly; tools/call and every other method require X-API-Key or Authorization: Bearer with the scoped MCP_API_KEY or the master API_KEY, and a missing or invalid credential on an execution call answers 401 with {"code": "invalid_api_key", "message": "A premium subscription or valid X-API-Key is required to execute scrapes."} while all billable work stays behind auth.

  • Direct — Claude web or Desktop: custom connector URL https://bintu-api.onrender.com/mcp with request header X-API-Key: <MCP_API_KEY>.
  • Direct — Claude Code: claude mcp add --transport http bintu https://bintu-api.onrender.com/mcp --header "X-API-Key: <MCP_API_KEY>".
  • MCPize (subscribers): connect to https://bintu-web-reader.mcpize.run with your MCPize sk_… key — the gateway injects the upstream token. No RapidAPI subscription needed; billing lives on the MCPize listing.

MCP_API_KEY is the optional scoped key set in the Render dashboard. It is accepted only on the MCP surfaces (/mcp, /mcp/tools, /mcp/tools/call) and never on the REST routes or /metrics, so it can be shared with connector users and rotated without touching the master API_KEY. While MCP_API_KEY is unset, the master API_KEY keeps working on /mcp exactly as before.

The server exposes one tool, bintu_scrape_text, with the same schema as the REST scrape. Tool execution failures (unsafe URL, rate limit, upstream error, render_js_disabled) come back in-band as isError: true with readable text so the model can correct itself. Execution requests (tools/call) require a valid key (the master API_KEY or the scoped MCP_API_KEY, via X-API-Key or Authorization: Bearer) and count against the same rate limit; discovery (initialize, tools/list, ping) needs no credentials. This endpoint is hidden from /docs; see the MCP section of the README.

Native Mobile MVP

The Expo project in mobile-app/ provides email sign-up/sign-in and a URL-to-readable-text screen for iOS and Android. User sessions are stored with the device's secure storage. The app sends its Supabase access token to POST /api/v1/mobile/scrape/text; the API verifies the user with Supabase, requires a verified email address, and applies the existing per-user burst limit. The Bintu API_KEY and RapidAPI provider secret are never sent to the app.

Configure the backend

  1. Create a Supabase project and enable email/password authentication. Keep email confirmation enabled.
  2. In Supabase project settings, copy the project URL and its publishable key (or legacy anon key). Do not use the service_role key or a secret key.
  3. Set SUPABASE_URL and SUPABASE_ANON_KEY in the Render service environment. Keep the existing generated API_KEY private.
  4. To enable persistent scraping cache and RapidAPI usage counts, set the server-only SUPABASE_SERVICE_ROLE_KEY in Render. Run supabase_schema.sql in the Supabase SQL editor to create the required tables and atomic usage-increment function. Never include the service-role key in the mobile app. Without this key, scraping continues with the existing bounded in-memory cache and database features are disabled.

The persistent cache retains successful Markdown for 24 hours. Cache keys include the URL, rendering mode, and extraction options, and expired entries are ignored. RapidAPI usage counts are incremented only for requests carrying a valid configured RapidAPI proxy secret and a valid X-RapidAPI-User header; cached successful calls are counted too. Database failures are logged and fall back to scraping or return the successfully extracted content.

The mobile proxy remains unavailable until both Supabase settings are configured. Supabase must be reachable by the API at request time so the API can verify each session.

In Supabase Authentication → URL Configuration → Redirect URLs, allow the Expo Go callback exp://**/--/auth/callback and the installed-app callback bintu://**. The app sends an explicit callback URL for sign-up confirmation and password recovery, so those links open Bintu instead of the default localhost:3000. Expo Go redirects only work while the development server is running and reachable on the phone's network.

Run on an Android phone

Install a current Node.js LTS release, install Expo Go on an Android phone, and from the mobile-app/ directory:

powershell
Copy-Item .env.example .env# Edit .env and set the Supabase project URL and publishable/anon key.npm installnode .\node_modules\expo\bin\cli start

Scan the QR code shown by Expo Go. The app handles confirmation and password-reset links and lets the user set a new password after following a recovery link. The EXPO_PUBLIC_ Supabase key is a client/public key, not a service secret; the API verifies sessions and keeps the Bintu server key private. A local .env file is git-ignored. iOS simulator builds require macOS and Xcode; public app-store distribution has separate developer enrollment requirements and fees.

This is an early MVP: the API's in-memory limiter resets on restart and does not provide account-abuse prevention or a monthly user quota. Review hosting and Supabase plan limits before inviting more users.

REST document reader

The reader provides paginated access to a retained text snapshot through POST /api/v1/reader/read and POST /api/v1/reader/continue. These routes are separate from the existing scrape endpoints and are not exposed as new MCP tools. They become available when a version containing this change is deployed.

Authentication and ownership

Both endpoints require either the configured direct API_KEY in X-API-Key, or a verified RapidAPI proxy request with a valid X-RapidAPI-User. Unauthenticated local development mode does not grant reader access.

Ownership is credential-level: all callers using the same direct API key share one owner identity. Verified RapidAPI subscribers have separate owner identities. An unverified subscriber header does not establish ownership. Mobile Supabase sessions and the scoped MCP_API_KEY are not reader credentials.

Treat continuation cursors as sensitive, temporary handles. Use the same authenticated owner for the initial read and subsequent continuations.

Read and continue

For local testing, start the application with an API_KEY configured, and set that same key in $env:BINTU_API_KEY. Use a deployed base URL only after the reader changes have been deployed.

powershell
$readerBaseUrl = "http://127.0.0.1:8000"$headers = @{ "X-API-Key" = $env:BINTU_API_KEY }
$body = @{    url = "https://www.iana.org/domains/reserved"    max_tokens = 1000    include_nav = $false    include_header = $false    include_footer = $false} | ConvertTo-Json
$page = Invoke-RestMethod `    -Method Post `    -Uri "$readerBaseUrl/api/v1/reader/read" `    -Headers $headers `    -ContentType "application/json" `    -Body $body
$page.text
while ($page.has_more) {    $body = @{        cursor = $page.next_cursor        max_tokens = 1000    } | ConvertTo-Json
    $page = Invoke-RestMethod `        -Method Post `        -Uri "$readerBaseUrl/api/v1/reader/continue" `        -Headers $headers `        -ContentType "application/json" `        -Body $body
    $page.text}

The initial request checks robots policy, fetches the page using the existing guarded fetcher, and extracts a text snapshot. Continuations read that snapshot without refetching the target. Pages therefore represent the original extraction, not subsequent website changes.

max_tokens must be a JSON integer from 100 through 50000; it defaults to 1000 on each request. It budgets page text only, not JSON framing, metadata, or other model context. A larger budget raises the effective extraction cap above the 6,000-character default, subject to a 200,000 character absolute ceiling. The response includes approx_tokens and token_method; do not treat the estimate as an exact count for every model tokenizer.

Responses include text, start_offset, end_offset, has_more, next_cursor, approx_tokens, token_method, boundary_split, and budget_scope (page_text_only). Continue using the returned cursor, not a client-supplied offset. The final page has has_more=false and next_cursor=null. The three extraction inclusion flags default to false and are set only on the initial request.

Retention and deployment limits

The reader retains at most 200,000 extracted text characters per document. It rejects larger extracted documents instead of silently shortening them. Its retention limit is separate from the legacy scrape response character limit; upstream fetch size and media-type guards still apply.

The application's default snapshot store has:

  • A fixed 600-second lifetime from snapshot creation. Reads do not extend it.
  • Capacity for 16 retained documents and 256 continuation handles per application store.
  • Oldest-inserted eviction when either capacity is exceeded.
  • Process-local memory only: snapshots are not persisted or shared between workers or instances, and disappear on restart.

Ten minutes is an expiry limit, not a guaranteed availability window: eviction can invalidate a cursor sooner. A continuation sent to another worker or instance cannot resolve the original worker's cursor. Multi-worker or multi-instance deployments need an explicit routing or shared-storage design before reliable continuation can be promised.

Unknown, expired, evicted, and wrong-owner cursors all return HTTP 404 with detail.code=reader_snapshot_unavailable. Start a new read to recover; doing so fetches the target again and may produce different text.

Both initial reads and continuations pass through the rate limiter. Successful verified RapidAPI requests on either endpoint attempt a developer usage increment when the database client is available. Database usage-recording failures do not fail an otherwise successful reader response. This accounting is not a replacement for gateway plan quotas.

Observability

Reader requests pass through the existing HTTP observability middleware: request count, error count, duration, request-completion logs, and the X-Request-ID response header.

The reader route implementation does not call the existing extraction-completion and extraction-failure metric helpers. Do not interpret those extraction-specific metrics as complete reader usage or failure coverage.

Tests

The suite lives in the tests/ package and is offline: it patches connections and uses fake responses, so it makes no live network requests.

powershell
python -m unittest discover -s tests -t . -p "test_*.py" -v

Integration tests run a real local HTTP server on a loopback port to exercise the full fetch, parse, and extract pipeline, with DNS resolution and connection targeting mocked so the SSRF guards still apply:

powershell
python -m unittest tests.test_integration -v

Chunking is covered by offline unit tests for token budgets, word boundaries, and fenced-code preservation, plus endpoint tests for token-budget validation, disabled JavaScript rendering, and successful chunked responses.

The synthetic golden fixtures in tests/fixtures/extraction_quality/ cover article, navigation-heavy, and table-heavy HTML without relying on third-party sites. Update a fixture and its expected .txt output only after reviewing an intentional extraction change.

GitHub Actions runs the offline test suite for pushes and pull requests. Render is configured to deploy linked commits only after CI checks pass. After deploying, verify real upstream fetching with python smoke_check.py (set BINTU_BASE_URL and BINTU_API_KEY); mocked tests cannot catch upstream HTTP regressions on their own.

Contributing

See CONTRIBUTING.md for local setup, tests, live smoke checks, and contribution expectations.

Operations

See docs/OPERATIONS.md for deployment checks, monitoring, incident response, and credential rotation.

Support

See docs/SUPPORT.md for troubleshooting authentication, URL restrictions, rate limits, truncation, low-yield pages, and upstream failures.

API reference

See docs/API_REFERENCE.md for the consolidated REST and MCP endpoint reference, request fields, response fields, limits, and error codes.

來源:README.md,提交 a2bb7ea

工具

0
該伺服器需要登入,因此 SourceWeft 無法在沒有帳號的情況下讀取其工具,也未收錄。已連接帳號所能看到的內容可能因帳號及其權限而異。

版本歷史

1
  1. v1.0.2最新Oct 22, 2026

評分

已滿足適用檢查項的 85%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通必需25 分
  • 已宣告認證方式必需10 分
  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分