Skip to main content
Glama
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).