Metadata-Version: 2.4
Name: indox-mcp
Version: 0.7.0
Summary: MCP server for the Indox document and media conversion API.
Author-email: Indox <support@indox.org>
License-Expression: LicenseRef-Proprietary
Keywords: indox,mcp,model-context-protocol,document-conversion
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: indox-client==0.7.0
Requires-Dist: mcp<3,>=2.3
Requires-Dist: uvicorn>=0.31
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Requires-Dist: responses>=0.23.0; extra == "test"
Requires-Dist: ruff==0.17.3; extra == "test"
Dynamic: license-file

# indox-mcp

<!-- mcp-name: org.indox/indox-mcp -->

MCP server for the [Indox](https://indox.org) conversion API. It lets an AI client — Claude Code,
Claude Desktop, Cursor, VS Code — convert documents, images, video, 3D models and fonts, check
credits, manage stored files and webhooks, and ask questions about PDFs. Every tool is a thin
wrapper over the official Python SDK, [`indox-client`](https://pypi.org/project/indox-client/).

**Requires:** Python ≥ 3.11 · an Indox API key (`ak_…`, created in the dashboard under **API Keys**)

It runs two ways from the same tools:

| Mode | Run | Auth | Files |
|---|---|---|---|
| Local | `indox-mcp` (stdio) | `INDOX_API_KEY` in the server's environment | read and written inside `INDOX_MCP_WORKSPACE_DIR` only |
| Hosted | `https://mcp.indox.org/mcp` (Streamable HTTP) | `Authorization: Bearer <your API key>` | `file_url` or `s3_key` in, `download_url` out |

## Tools

| Tool | What it does | Mode |
|---|---|---|
| `health` | Check that the Indox API is reachable | both |
| `credits_balance` | Remaining credits — check before large batches | both |
| `usage_summary` | Recent conversion usage | both |
| `list_formats` | Input and output formats for a kind: `image`, `video`, `model`, `pdf`, `font` | both |
| `convert_file` | Start a conversion from one source: `file_path`, `file_url` or `s3_key`. Spends credits | both (`file_path` local only) |
| `get_conversion` | Current status of a conversion | both |
| `wait_conversion` | Poll until it finishes or the time limit passes (`finished=false` → call again) | both |
| `download_result` | Local: save the result into the workspace, never overwriting. Hosted: return its link | both |
| `list_files` | Files in your Indox storage | both |
| `create_upload_url` | A presigned upload URL; pass the returned `s3_key` to `convert_file` | both |
| `list_webhooks` · `create_webhook` · `test_webhook` · `delete_webhook` | Manage webhooks. The signing secret is never returned to the model | both |
| `pdf_form_fields` | List the fillable fields of a local PDF | local |
| `pdf_ai_ask` | Ask a question about a local PDF. Spends credits | local |
| `pdf_ai_job` | Status and answer of a `pdf_ai_ask` job | both |

Tools that spend credits or change data are marked as not read-only, so clients can ask before
running them. Text extracted from your documents is treated as data, never as instructions.

## Local mode (stdio)

```bash
pip install indox-mcp
```

The server starts only when every variable below is set, and names the first one missing.

| Variable | Value |
|---|---|
| `INDOX_API_KEY` | your `ak_…` key |
| `INDOX_BASE_URL` | `https://indox.org` |
| `INDOX_MCP_WORKSPACE_DIR` | absolute path of the folder the server may read from and save results to |
| `INDOX_MCP_WAIT_MAX_SECONDS` | longest one `wait_conversion` call may poll, e.g. `120` |
| `INDOX_MCP_POLL_SECONDS` | seconds between status checks, e.g. `2` |

If a client cannot find `indox-mcp`, use the full path that `which indox-mcp` prints.

## Hosted mode

The hosted server is **not live yet**; it will be at `https://mcp.indox.org/mcp`. Send your API key
as `Authorization: Bearer ak_…`; each call runs with that key alone.

**Downloads:** a hosted `download_result` returns a link with `needs_api_key=true` — fetch it with
the same API key. Key-free signed links are planned; until they ship, hosted downloads need the
caller's key.

## Client setup

Replace `ak_your_key` and `/absolute/path/to/files` with your own values.

**Claude Code** — local, then hosted:

```bash
claude mcp add --env INDOX_API_KEY=ak_your_key --env INDOX_BASE_URL=https://indox.org \
  --env INDOX_MCP_WORKSPACE_DIR=/absolute/path/to/files \
  --env INDOX_MCP_WAIT_MAX_SECONDS=120 --env INDOX_MCP_POLL_SECONDS=2 \
  --transport stdio indox -- indox-mcp

claude mcp add --transport http indox-hosted https://mcp.indox.org/mcp \
  --header "Authorization: Bearer ak_your_key"
```

Add `--scope project` to share the server through `.mcp.json`, or `--scope user` for every project.

**Claude Desktop** — Settings › Developer › Edit Config opens `claude_desktop_config.json`
(macOS `~/Library/Application Support/Claude/`, Windows `%APPDATA%\Claude\`):

```json
{
  "mcpServers": {
    "indox": {
      "command": "indox-mcp",
      "env": {
        "INDOX_API_KEY": "ak_your_key",
        "INDOX_BASE_URL": "https://indox.org",
        "INDOX_MCP_WORKSPACE_DIR": "/absolute/path/to/files",
        "INDOX_MCP_WAIT_MAX_SECONDS": "120",
        "INDOX_MCP_POLL_SECONDS": "2"
      }
    }
  }
}
```

The hosted server is added as a custom connector (Customize › Connectors › Add custom connector),
not in this file. An API key goes under **Request headers** as `Bearer ak_your_key`; that option is
in beta, and on Team and Enterprise plans every member would share one key — use local mode there.

**Cursor** — `.cursor/mcp.json` in a project, or `~/.cursor/mcp.json` for every project:

```json
{
  "mcpServers": {
    "indox": {
      "type": "stdio",
      "command": "indox-mcp",
      "env": {
        "INDOX_API_KEY": "${env:INDOX_API_KEY}",
        "INDOX_BASE_URL": "https://indox.org",
        "INDOX_MCP_WORKSPACE_DIR": "/absolute/path/to/files",
        "INDOX_MCP_WAIT_MAX_SECONDS": "120",
        "INDOX_MCP_POLL_SECONDS": "2"
      }
    },
    "indox-hosted": {
      "url": "https://mcp.indox.org/mcp",
      "headers": { "Authorization": "Bearer ${env:INDOX_API_KEY}" }
    }
  }
}
```

**VS Code** (Copilot agent mode) — `.vscode/mcp.json`; VS Code prompts for the key and hides what you type:

```json
{
  "inputs": [
    { "type": "promptString", "id": "indox-key", "description": "Indox API key", "password": true }
  ],
  "servers": {
    "indox": {
      "type": "stdio",
      "command": "indox-mcp",
      "env": {
        "INDOX_API_KEY": "${input:indox-key}",
        "INDOX_BASE_URL": "https://indox.org",
        "INDOX_MCP_WORKSPACE_DIR": "/absolute/path/to/files",
        "INDOX_MCP_WAIT_MAX_SECONDS": "120",
        "INDOX_MCP_POLL_SECONDS": "2"
      }
    },
    "indox-hosted": {
      "type": "http",
      "url": "https://mcp.indox.org/mcp",
      "headers": { "Authorization": "Bearer ${input:indox-key}" }
    }
  }
}
```

## Run the HTTP server yourself

`indox-mcp --http` serves Streamable HTTP at `/mcp` and an open `/healthz`. Each request uses the
caller's own key; the server holds no key of its own. Required variables:

| Variable | Value |
|---|---|
| `INDOX_BASE_URL` | `https://indox.org` |
| `MCP_HOST` · `MCP_PORT` | bind address and port |
| `MCP_ALLOWED_HOSTS` | comma-separated `Host` values to accept, e.g. `mcp.example.com` |
| `MCP_ALLOWED_ORIGINS` | comma-separated browser origins to accept |
| `MCP_KEY_CACHE_SECONDS` · `MCP_KEY_CACHE_SIZE` | how long, and how many, verified keys are remembered (hashed, in memory) |
| `INDOX_MCP_WAIT_MAX_SECONDS` · `INDOX_MCP_POLL_SECONDS` | as in local mode; keep the wait low when hosted |

Client config formats checked on 2026-10-29 against
[Claude Code](https://code.claude.com/docs/en/mcp),
[Claude Desktop](https://modelcontextprotocol.io/docs/develop/connect-local-servers) and
[custom connectors](https://claude.com/docs/connectors/custom/remote-mcp),
[Cursor](https://cursor.com/docs/context/mcp) and
[VS Code](https://code.visualstudio.com/docs/copilot/reference/mcp-configuration).
