jinaju
by xdqi
README.md
# jinaju
A drop-in MCP server compatible with the [Jina AI MCP server](https://mcp.jina.ai/v1), backed by a stealth Chromium ([cloakbrowser](https://www.npmjs.com/package/cloakbrowser)) + [CapSolver](https://capsolver.com) extension, with ordered fallback to a pure MCP proxy of Jina.
jinaju exposes the same 21 tools as the Jina MCP server with identical JSON-RPC shapes, so an MCP client configured against `mcp.jina.ai/v1` can point at jinaju with zero changes. The difference is the backend: by default the cloak-primary tools (`read_url`, `parallel_read_url`, `capture_screenshot_url`) are served by a real headless Chromium that renders the page and auto-solves captchas via CapSolver, falling back to the Jina MCP proxy if the cloak path fails.
## Install
```bash
npm install -g jinaju
```
For development:
```bash
git clone https://github.com/xdqi/jinaju.git
cd jinaju
npm install
npm run build
```
`cloakbrowser` resolves its stealth Chromium from `~/.cloakbrowser` (downloaded on first launch if missing). The CapSolver extension is fetched from its [GitHub release](https://github.com/capsolver/capsolver-browser-extension/releases) on first launch.
## Configure
Create `jinaju.config.jsonc` (gitignored — it holds tokens) at the project root. The full schema is at [`docs/superpowers/specs/jinaju.config.schema.json`](docs/superpowers/specs/jinaju.config.schema.json). Key fields:
```jsonc
{
"server": { "host": "127.0.0.1", "port": 9334 },
"auth": { "bearerTokens": [], "stdioClientId": "stdio" },
"jina": { "mcpUrl": "https://mcp.jina.ai/v1", "token": "jina_..." },
"capsolver": { "token": "CAP-...", "extDir": "./vendor/capsolver-ext", "solveTimeoutMs": 15000 },
"browser": {
"chromePath": null,
"userDataDir": "./.browser-profile",
"headless": true,
"stealthArgs": true,
"licenseKey": null,
"maxParallel": 5
},
"tools": {
"read_url": { "backend": ["cloak", "jina"] },
"parallel_read_url": { "backend": ["cloak", "jina"] },
"capture_screenshot_url": { "backend": ["cloak", "jina"] },
"extract_pdf": { "backend": ["jina"] }
}
}
```
- `auth.bearerTokens`: empty list → a `jinaju_<32>` token is generated at startup and printed to stderr once. Configure a list to pin tokens (supports multiple clients / revocation).
- `jina.token`: your Jina API key.
- `capsolver.token`: your CapSolver API key (injected into the extension's `assets/config.js` at startup — no manual extension setup).
- `browser.chromePath`: `null` → use the cloakbrowser-resolved Chromium; set a path to override.
- `tools.<name>.backend`: ordered backend list. `["cloak", "jina"]` = try cloak, fall back to jina on failure. `["jina"]` = jina only. Missing tools default to `["jina"]`.
- `show_api_key` is not configurable here — it always returns the caller's own token (http: the bearer; stdio: `stdioClientId`).
This version does **not** read environment variables; all values come from the config file.
## Run
```bash
jinaju # http mode (default subcommand)
jinaju http # explicit http mode — Streamable HTTP + SSE on :9334/mcp
jinaju stdio # stdio mode — auth-free, local process
jinaju --config /path/to.jsonc http # explicit config + http mode
```
## Transports
- **http** (`jinaju http`): stateless Streamable HTTP at `POST /mcp`, bearer-token auth. Mirrors `mcp.jina.ai/v1` — an MCP client pointed at Jina can switch to `http://<host>:<port>/mcp` with the jinaju bearer token. Also serves `GET /s/<id>.jpg` for `capture_screenshot_url`'s `return_url: true` option (in-memory image host).
- **stdio** (`jinaju stdio`): stdin/stdout JSON-RPC, auth-free (local process trust). `capture_screenshot_url` with `return_url: true` returns `file://` paths to a temp dir.
Both transports expose the same 21 tools with identical schemas to Jina. The browser launches only if a configured tool uses `cloak`; a pure-jina config (e.g. `tools: {}`) skips the browser and CapSolver provisioning entirely.
## Fallback semantics
Each tool's `backend` list is tried in order. A cloak attempt is considered *failed* (and falls through to the next backend) when:
- the browser hard-fails (crash, navigation timeout, 4xx/5xx, DNS), or
- CapSolver does not clear a captcha within `capsolver.solveTimeoutMs`, or
- an uncaught exception occurs.
Content-quality is **not** compared — if cloak produces a valid result, it's used even if Jina's would be better (no double-fetch). Search/embedding/reranker tools (`search_web`, `classify_text`, `deduplicate_*`, `sort_by_relevance`, etc.) are jina-only by default (cloak has no equivalent).
## Architecture
```
client ──http/stdio──▶ MCP Server (SDK)
│
▼
ToolRouter ── per-tool ordered backend list
│
┌──────────┴──────────┐
▼ ▼
CloakBackend JinaBackend
(cloakbrowser + (pure MCP-to-MCP proxy
CapSolver ext) to mcp.jina.ai/v1)
```
See [`docs/superpowers/specs/2026-07-04-jinaju-mcp-design.md`](docs/superpowers/specs/2026-07-04-jinaju-mcp-design.md) for the full design and [`docs/superpowers/plans/2026-07-04-jinaju-mcp.md`](docs/superpowers/plans/2026-07-04-jinaju-mcp.md) for the implementation plan.
## Development
```bash
npm run typecheck # tsc -p tsconfig.test.json (typechecks src + tests)
npm test # builds, then runs vitest (83 tests, incl. real-browser integration + e2e)
```
Tests include real-browser integration (cloakbrowser + CapSolver against example.com) and end-to-end http/stdio transport tests (against a stub jina to stay offline).
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues