Metadata-Version: 2.4
Name: indiquant-mcp
Version: 3.3.5
Summary: Local MCP server that lets Claude work with the IndiQuant tournament through your own SDK session. Data stays on your disk.
Project-URL: Homepage, https://platform.indiquantresearch.in
Author: IndiQuant Research
License-Expression: MIT
License-File: LICENSE
Keywords: claude,indiquant,machine-learning,mcp,tournament
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.11
Requires-Dist: indiquant-sdk==3.3.5
Requires-Dist: mcp>=1.2
Description-Content-Type: text/markdown

# indiquant-mcp

A local [MCP](https://modelcontextprotocol.io) server that lets Claude (Claude
Desktop or Claude Code) work with the IndiQuant tournament for you: read the
current round, download its features to your disk, run your own model script,
validate predictions, and submit them — with you confirming every step that
spends something.

It runs on your machine, over stdio, as a child process of your Claude client.
It talks to the platform through `indiquant-sdk` and nothing else.

---

## Install

```
pip install indiquant-mcp        # pulls in indiquant-sdk==3.3.5 and mcp
```

Python 3.11 or newer. The `indiquant-mcp` command is installed on your `PATH`.
(From wheels instead: `pip install indiquant_sdk-3.3.5-py3-none-any.whl
indiquant_mcp-3.3.5-py3-none-any.whl`.)

## Sign in — in your own terminal, once

```
indiquant-mcp login
```

This asks for your email and password **in your terminal** (the password is
not echoed), and:

1. signs in and saves the token pair to `~/.config/indiquant/tokens.json`,
   readable only by you (mode 0600);
2. creates this machine's install key at `~/.indiquant/install.key` (Ed25519,
   mode 0600) if there is none;
3. registers that key with your account, once. Registration re-checks your
   password (that is the reason it happens here and not inside Claude), and the
   platform emails you a notice naming the key.

Claude never sees your password or your tokens: no tool accepts or returns
either. `indiquant-mcp login` refuses to run without an interactive terminal,
so it cannot be driven through a pipe.

Other commands: `indiquant-mcp whoami` (account, key, models),
`indiquant-mcp logout` (deletes the saved token pair; the install key stays,
because it identifies this machine — revoke it on the platform if you no longer
control the machine).

## Connect Claude

**Claude Code**

```
claude mcp add indiquant -- indiquant-mcp serve
```

**Claude Desktop** — add to `claude_desktop_config.json` (Settings → Developer →
Edit Config), then restart Claude Desktop:

```json
{
  "mcpServers": {
    "indiquant": {
      "command": "indiquant-mcp",
      "args": ["serve"]
    }
  }
}
```

If Claude Desktop cannot find the command, use the absolute path that
`which indiquant-mcp` prints. To point at another API origin, add
`"env": {"INDIQUANT_API_BASE": "https://…"}`; the default is
`https://api.indiquantresearch.in`.

Then ask Claude something like *"Submit this round's predictions for my model"*,
or pick the **submit_this_round** prompt, which walks the whole sequence.

---

## Your data stays on your disk

The round files and training artefacts are licensed to you under the
platform's data-access terms (ask Claude for `get_data_access_terms`). This
server is built so that they never enter the conversation:

* **Downloads write to a path you name** (`.parquet` or `.csv`) and return only
  a summary: row count, column count, the feature list, the universe size, the
  dataset version and the path. No feature value is ever returned to Claude.
* **Rows are kept in your data session's own order.** That order is one of the
  session's attribution marks; the file is not sorted, and you should not
  re-sort a copy you share anywhere (you should not share one at all — see the
  terms).
* **Sessions are budgeted, so downloads are cached.** A data session lasts 15
  minutes and serves at most three passes of its pages, and an account may open
  48 in any rolling 24 hours. The server takes one pass, caches the result under
  `~/.cache/indiquant-mcp/`, and answers every later request for the same round
  or version from that cache without opening a new session. It also counts the
  sessions this machine opened and refuses a 49th before the platform has to.
* **Your model runs locally.** `run_local_model` runs your own Python script as
  a subprocess (with a timeout), which reads the features file and writes an
  `id,score` CSV. Claude sees only the exit status and the validation summary;
  the script's output goes to a `.log` file beside the predictions.

## Nothing is spent without you

* **Predictions come from a file**, with the exact header `id,score` — never
  typed by Claude.
* **Every submission is validated locally first**, with the same validator the
  platform runs (`indiquant.core.integrity.validate_submission`), plus the
  payload size limit. A file that fails is never sent, and that costs nothing.
  A rejection by the server, by contrast, uses one of your **10 attempts per
  round per model**.
* **`submit_predictions` needs `confirm=true` and the preview's
  `confirmation_token`.** Called without `confirm`, it returns a preview — round,
  model, row count, deadline, attempts remaining — and a `confirmation_token`,
  and Claude is instructed to show you that and ask before submitting. The token
  is bound to the round, the model and the float32 digest of your file's scores
  in universe order, and keyed to this server process: a confirmed call without
  it, or after the file has changed, or with a token from another session, is
  refused and nothing is sent. After submission it shows the receipt and checks
  the receipt's sha256 against the same digest.
* `create_model` (one of your 3 model slots; the name is public and unique) and
  `revoke_install_key` (irreversible) are confirm-gated the same way: the
  preview's `confirmation_token` is bound to the name (or the key and reason)
  you were shown.

## Tools

| Tool | What it does | Spends / writes |
|---|---|---|
| `get_current_round` | Latest round on a track: state, window open, deadline in IST and UTC | — |
| `get_round_status` | One round's state and schedule | — |
| `get_round_descriptor` | Universe size, feature list, fingerprint, dataset version (ids only with `include_universe=true`) | — |
| `get_scoring_rules` | The published scoring rules | — |
| `get_data_access_terms` | The versioned data-access terms | — |
| `list_models` | Your models and remaining slots (max 3) | — |
| `create_model` | Create a model (preview, then `confirm=true` + token) | one model slot |
| `list_install_keys` | Your install keys, marking this machine's | — |
| `revoke_install_key` | Revoke a key (preview, then `confirm=true` + token) | irreversible |
| `download_round_features` | Round features to your path; summary only | one data session (unless cached); writes a file |
| `download_training_data` | Training artefact to your path; summary only | one data session (unless cached); writes a file |
| `validate_predictions` | Full local validation report for an `id,score` file | — |
| `export_predictions_csv` | Normalise a parquet/CSV to the exact CSV the website's upload form takes | writes a file |
| `submit_predictions` | Validate, preview, then submit on `confirm=true` + token; receipt + sha256 check | one attempt |
| `get_scores` | A model's resolved round scores | — |
| `get_trust` | A model's latest trust snapshot | — |
| `get_leaderboard` | The published leaderboard (`core` or `sprint`) | — |
| `run_local_model` | Run your script locally with a timeout; returns the validation summary | runs local code; writes a file |

Hosted execution (uploading a model for the platform to run) is not offered
here.

### Your model script

`run_local_model` calls it, with the Python interpreter `indiquant-mcp` itself
runs on, as

```
python your_model.py --features FEATURES.parquet --out PREDICTIONS.csv --round-id ROUND_ID
```

and also sets `INDIQUANT_FEATURES_PATH`, `INDIQUANT_PREDICTIONS_PATH` and
`INDIQUANT_ROUND_ID`. Those three are the only `INDIQUANT_*` variables your
script sees: every other one in the server's environment (an API token, a
password, the token-file path) is removed before it starts. The script must be
an existing `.py` file; `--out` must end `.csv`, and an existing file there (or
its `.csv.log`) is replaced only with `overwrite=true`. The features parquet is indexed by `id`. Write every id
once, with a finite score; scores are ranks, so only their order matters.

```python
import argparse
import pandas as pd

p = argparse.ArgumentParser()
p.add_argument("--features"); p.add_argument("--out"); p.add_argument("--round-id")
args = p.parse_args()

features = pd.read_parquet(args.features)
scores = my_model.predict(features)            # your model here
pd.DataFrame({"id": features.index, "score": scores}).to_csv(args.out, index=False)
```

If your model needs packages from another environment, install `indiquant-mcp`
into that environment (or install the packages into this one); the tool does not
accept an interpreter path, because a path chosen in conversation could run any
program rather than your model.

---

## Files and settings

| Path | What | Override |
|---|---|---|
| `~/.config/indiquant/tokens.json` | token pair, mode 0600 | `INDIQUANT_MCP_TOKEN_FILE` |
| `~/.indiquant/install.key` | this machine's install key, mode 0600 | `INDIQUANT_INSTALL_KEY` |
| `~/.cache/indiquant-mcp/` | cached downloads and a local ledger of sessions and receipts | `INDIQUANT_MCP_CACHE_DIR` |
| API origin | `https://api.indiquantresearch.in` | `INDIQUANT_API_BASE` |

**Refresh tokens are single-use**, and presenting a used one revokes your whole
sign-in and alerts an operator. Claude Desktop and Claude Code each start their
own server, so the servers coordinate through a lock file beside
`tokens.json`: each re-reads the file before every call, and a rotated pair is
written exactly once, atomically, before it is used. If a rotation is
interrupted (the reply never arrives), the server discards the pair rather than
risk presenting it twice, and asks you to run `indiquant-mcp login` again.

## Troubleshooting

* *"not signed in"* — run `indiquant-mcp login` in a terminal.
* *"this install has no key yet"* — the same.
* *`data_access_suspended`* — the operator has withdrawn data access for your
  account; a new key or session will not restore it. Contact the operator.
* *`round_file_withheld`* — this deployment does not serve the round file for
  local download.
