Metadata-Version: 2.4
Name: indiquant-mcp
Version: 3.5.0
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==3.5.0
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 the `indiquant` SDK and nothing else.

---

## Install

```
pip install indiquant-mcp        # pulls in indiquant==3.3.7 and mcp
```

Python 3.11 or newer. The `indiquant-mcp` command is installed on your `PATH`.
(From wheels instead: `pip install indiquant-3.3.7-py3-none-any.whl
indiquant_mcp-3.3.7-py3-none-any.whl`.) The SDK was published as
`indiquant-sdk` up to 3.3.5; if an older install has it, `pip uninstall
indiquant-sdk` first.

## 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. The key works only once that
   notice has gone out; if the platform could not send it, `login` says so —
   run `indiquant-mcp login` again a few minutes later and the same pending
   key is confirmed with the password you type (the SDK's `indiquant login`
   step, shared). A key that was revoked is set aside
   (`install.key.revoked-<time>`) and a new one is made;
4. shows you the **data-access terms** in force — their version and every
   clause, as the platform serves them — and asks `[y/N]` whether you accept
   them. Downloads are refused until your account has accepted the version in
   force, and a new version is asked about again. Declining is fine — so is
   Ctrl-D or Ctrl-C at the prompt: you stay signed in, and
   `indiquant-mcp accept-terms` asks again whenever you are ready. If the
   terms change while they are being shown (version or digest), nothing is
   asked or accepted; run it again. A key that is not live yet does not stop
   the acceptance: it is recorded without 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.

**Claude cannot accept the terms for you.** No tool accepts them. If you ask
Claude to download data before accepting, the tool refuses and Claude is told
to ask you to run `indiquant-mcp accept-terms` in your own terminal.

Other commands: `indiquant-mcp accept-terms` (read and accept the data-access
terms in force), `indiquant-mcp whoami` (account, key, terms, 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. There is no per-round
  attempt limit: a new submission replaces the earlier one for that round, and
  each model may make at most 30 upload attempts an hour.
* **`submit_predictions` needs `confirm=true` and the preview's
  `confirmation_token`.** Called without `confirm`, it returns a preview — round,
  model, row count, deadline — 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, and the platform's immediate
  feedback on the upload (feature exposure, near-duplicates of your other
  models, notes).
* `create_model` (the name is public and unique; there is no cap on models) 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, and whether your account accepted them (it never accepts them) | — |
| `list_models` | Your models | — |
| `create_model` | Create a model (preview, then `confirm=true` + token) | a new, public model name |
| `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 + feedback | replaces the round's earlier submission |
| `get_scores` | A model's resolved round scores | — |
| `get_trust` | A model's latest trust snapshot | — |
| `get_model_report` | A model's performance report: headline, per-round diagnostics, regimes, cost realism, next score date, what to try next | — |
| `get_overview` | All your models side by side, with near-duplicate groups and the grant rule | — |
| `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_terms_not_accepted`* — your account has not accepted the data-access
  terms in force (a new version has to be accepted again). Run `indiquant-mcp
  accept-terms` in a terminal; Claude cannot do it for you.
* *"could not email the notice"* at login (`mail_delivery_failed`) — the key is
  registered but not live yet. Run `indiquant-mcp login` again in a few minutes.
* *"this install's key cannot be used"* at login — the key was revoked or is
  held elsewhere; the message says what to do (usually: move
  `~/.indiquant/install.key` aside and log in again).
* *`watermark_drift`* — the platform refused to serve a page it could not
  re-render exactly; ask again (a new session is opened). Report it if it
  recurs.
* *`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.
