indiquant-mcp
A local MCP 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:
- signs in and saves the token pair to
~/.config/indiquant/tokens.json, readable only by you (mode 0600); - creates this machine's install key at
~/.indiquant/install.key(Ed25519, mode 0600) if there is none; - 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:
{
"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 (
.parquetor.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_modelruns your own Python script as a subprocess (with a timeout), which reads the features file and writes anid,scoreCSV. Claude sees only the exit status and the validation summary; the script's output goes to a.logfile 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_predictionsneedsconfirm=trueand the preview'sconfirmation_token. Called withoutconfirm, it returns a preview — round, model, row count, deadline, attempts remaining — and aconfirmation_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) andrevoke_install_key(irreversible) are confirm-gated the same way: the preview'sconfirmation_tokenis 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.
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 loginin 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.
Release files for indiquant-mcp 3.3.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| indiquant_mcp-3.3.5-py3-none-any.whl | Python 3 | none | any | Details |
Release files / indiquant_mcp-3.3.5-py3-none-any.whl
| Download URL | indiquant_mcp-3.3.5-py3-none-any.whl |
|---|---|
| Size | 37.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7bc454daa5beab7c6cac077cc3f5fbb13bb538f215db04dc145b24999dd0c4ac
|
|
BLAKE2b-256 checksum How to use checksums |
48330a07479f234a7814b5f60c73dff2754c98b374a37c12de05b691133f6391
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.6
|