# Amatya Tests — documentation Online test platform by SIDDHI xSYS. Organizer app: https://siddhixsys.com/tests/app — MCP server: https://siddhixsys.com/api/tests-mcp (Bearer token auth; see the "Connect your AI agent" section). --- # Amatya Tests — Quickstart Amatya Tests lets anyone — a tutor, a YouTube teacher, an institute, or an exam agency — run online tests that students take on their phones. Question bank, sectioned papers with JEE-style marking, Hindi + English in one test, auto-evaluation, and results you can export or publish. ## The 10-minute path (by hand) 1. **Sign in** at [/tests/app/login](/tests/app/login) — email code or Google, no password. 2. **Add questions** in the Question bank tab (or paste JSON — see [JSON import](/docs/json-import)). 3. **Create a test**, add a section with its marking scheme, attach questions. 4. **Add students** (paste `name, phone, email` lines) or switch on *Open to anyone*. 5. **Publish** — every student gets a personal link; share by email, WhatsApp, or copy. 6. **Results** land in the Results tab as students submit; publish them when ready. ## The 2-minute path (with your AI agent) Connect Claude Code, Cursor, or any MCP-capable agent to your account ([setup](/docs/mcp)), then say: > "Here's a folder of question papers. Add every question to my Amatya Tests > bank in English and Hindi, build a 60-minute test with JEE marking from the > mechanics questions, publish it for my list, and send the links." The agent inserts, **verifies its own work** against the per-item report, builds, publishes, and can even take a preview attempt to check the paper before your students see it. ## Free plan 3 published tests (lifetime) · 40 students per test · 75-question bank · full MCP/API access. [Packs and Pro](/docs/pricing) when you outgrow it. --- # Connect your AI agent (MCP) Amatya Tests speaks [MCP](https://modelcontextprotocol.io) — the open protocol coding agents use to call tools. Connect once and your agent can ingest question papers, build tests, send links, and evaluate written answers, all against your real account with your real limits. ## Setup 1. Mint an **API token** in [Agents & API](/tests/app/tokens). It is shown once — store it like a password. 2. Add the server to your agent: **Claude Code** ```bash claude mcp add amatya-tests --transport http https://siddhixsys.com/api/tests-mcp --header "Authorization: Bearer atk_YOUR_TOKEN" ``` **Cursor / any HTTP MCP client** — `.cursor/mcp.json`: ```json { "mcpServers": { "amatya-tests": { "url": "https://siddhixsys.com/api/tests-mcp", "headers": { "Authorization": "Bearer atk_YOUR_TOKEN" } } } } ``` **stdio-only clients** — bridge with `npx mcp-remote https://siddhixsys.com/api/tests-mcp --header "Authorization: Bearer atk_YOUR_TOKEN"`. ## The loop good agents run 1. `get_limits` — know the caps before starting. 2. `add_questions` — batch insert with a `client_ref` per question; **read the per-item report** (inserted / exists / error) instead of assuming success. Retries with the same `client_ref` are no-ops, never duplicates. 3. `create_test` → `create_section` (marking scheme) → `add_test_questions` (explicit ids, or a random `pool` by topic/difficulty that always respects the test's languages). 4. `publish_test` — validation errors name exactly what to fix; `language_gaps` lists which question is missing which language. 5. `get_preview_link` — **take the test yourself** (the preview attempt never counts) and sanity-check the paper. 6. `add_takers` → `get_links` / `send_invites` (previews first; queuing real email requires `confirm: true` — ask your human). 7. After submissions: `list_attempts` → `get_attempt` (answers beside the rubric) → `evaluate_answer` → `publish_results`. Every response ends with your remaining entitlements, so a well-behaved agent never discovers a cap by failing. ## Rate limits & scopes Free accounts: 60 tool calls/minute. Pro: 240. Tokens carry scopes (`bank:write`, `tests:write`, `results:write`, `send`, …) — a [delegate token](/docs/tokens) is scope-narrowed to the bank only. --- # JSON import No agent connected? Ask any LLM to convert your question paper into this JSON, then paste it in **Question bank → Import JSON** (or POST it to `/api/tests/bank/import`). ## The prompt that works > Convert this question paper into JSON matching the schema below. One object > per question. Give every question a topic and difficulty. Use option ids > a, b, c, d. Put the correct answers in answer_spec. Reply with ONLY the JSON. ## Schema ```json { "questions": [ { "type": "mcq_single", "answer_spec": { "correct": ["b"] }, "topic": "physics", "subtopic": "kinematics", "difficulty": "easy", "tags": ["neet"], "client_ref": "paper1-q1", "translations": [ { "lang": "en", "body": "Acceleration due to gravity near Earth?", "options": [ { "id": "a", "text": "8.9 m/s²" }, { "id": "b", "text": "9.8 m/s²" } ] }, { "lang": "hi", "body": "पृथ्वी के निकट गुरुत्वीय त्वरण?", "options": [ { "id": "a", "text": "8.9 m/s²" }, { "id": "b", "text": "9.8 m/s²" } ] } ] } ] } ``` ## answer_spec by type | type | answer_spec | | --- | --- | | `mcq_single` | `{"correct":["b"]}` — exactly one id | | `mcq_multi` | `{"correct":["a","c"]}` — every correct id | | `integer` | `{"value": 42}` | | `numeric` | `{"value": 9.8, "tolerance": 0.1}` | | `true_false` | `{"value": true}` | | `fill_blank` | `{"accepted": ["Delhi"]}` (or per-language `fill_accepted`) | | `long_answer` | `{"rubric": "what earns marks — only you see this"}` | A `numeric` question can also list **several acceptable answers or ranges** — real answer keys need this ("9 or 6 both correct", "anything from 0.30 to 0.33"): ```json { "accepted_values": [ { "value": 9 }, { "value": 6 } ] } { "accepted_values": [ { "min": 0.30, "max": 0.33 } ] } ``` ## Math and diagrams in question text Question bodies and option texts render **LaTeX math**: `$...$` inline, `$$...$$` on its own line. Write `\$` for a literal dollar sign. ```text The value of $\int_0^1 x^2\,dx$ is $$\frac{1}{3}$$ ``` Diagrams: upload the image first (`POST /api/tests/bank/assets`, or the `upload_asset` MCP tool), then reference the returned URL from the body with `![caption](/api/tests/asset//)`. Only Amatya asset URLs render — external image URLs stay as plain text by design. Rules the importer enforces: option **ids must match across languages** (that is what lets a Hindi answer grade identically to an English one), `correct` ids must exist in the options, and `client_ref` makes re-imports idempotent. The import report tells you per question what happened — read it. --- # Marking schemes Marking is configured on each **section**, and any question can override it. Two schemes, three numbers — that combination covers everything from a school quiz to a JEE mock. ## standard - Fully correct → **+correct** - Anything else → **−negative** - Unanswered → **0**, always. Negative marking punishes wrong answers, never silence. ## jee_partial (for multi-correct MCQs) With `{"correct": 4, "per_correct": 1, "negative": 2}`: | Student selects | Correct answer a,c | Marks | | --- | --- | --- | | a, c | all correct | **+4** | | a | clean subset | **+1** (1 × per_correct) | | a, b | any wrong option | **−2** | | nothing | unanswered | **0** | Single-correct, integer, numeric, true/false and fill-blank questions grade full-or-negative under either scheme. ## Where marks come from - `numeric` accepts a **tolerance**: `{"value": 9.8, "tolerance": 0.1}` — and alternative keys or ranges via `accepted_values` (see JSON import). - `fill_blank` accepts any listed answer, case- and spacing-insensitive, in **any language** the question carries. - `long_answer` is marked by you (or your agent) against the rubric; marks clamp between −negative and the question's maximum. Published tests grade against a **frozen copy** of every question — editing the bank later never changes how a live test scores. --- # Languages A question is **one logical question** with any number of language versions. Options share stable ids across languages, so a student answering in Hindi is graded by exactly the same key as one answering in English. ## How it flows 1. Add each question in the languages you teach (the bank form has language tabs; imports and agents pass a `translations` array). 2. A test declares its **conduct languages** (e.g. English + हिन्दी). 3. **Publish blocks** until every attached question covers every conduct language — the error lists exactly which question is missing which language. Random pooling only ever picks questions that already qualify. 4. Students pick a language at the start and can **switch on any question** (the JEE pattern) — their answers carry over, because answers are option ids, not text. ## Notes - `fill_blank` can carry per-language accepted answers; grading accepts the union, so "Delhi", "दिल्ली" and "Dilli" can all be right. - Language codes are ISO-639-1: `en`, `hi`, `bn`, `ta`, `te`, `mr`, … --- # Anti-cheat & proctoring Every test picks one of three levels. Signals are surfaced to you; nothing auto-disqualifies a student — you judge. | Level | What it does | | --- | --- | | **light** | Server-side timing, one submission, resume-safe autosave | | **standard** (default) | + per-student question & option shuffling, tab/app-switch counting, paste discouragement, submission metadata | | **heavy** (Pro) | + required fullscreen where the platform supports it, webcam snapshots on a timer and on tab-switch, consent screen | ## What this honestly cannot do No online proctoring can stop a second phone on the desk, a person sitting off-camera, or a determined impersonator. Heavy mode **deters and documents** — snapshot timelines and integrity counts let you confront anomalies — but it is evidence, not prevention. We would rather tell you this than sell you certainty that does not exist. ## Data care Snapshots are compressed, visible only to the test's organizer, and deleted after 30 days. Students consent on-screen before the camera starts; by enabling heavy mode you confirm you are entitled to collect this from your students (for minors, that responsibility is yours as the institution). Timing is enforced by the server clock — a student's device clock changes nothing. Embedded players cannot force fullscreen, so heavy tests downgrade to standard inside embeds and you are warned at publish. --- # API & delegate tokens Minted in [Agents & API](/tests/app/tokens). Raw tokens are shown **once** and stored hashed — treat them like passwords, revoke them any time. ## API tokens (`atk_…`) Full-scope by default, no expiry. For your own agents and scripts. Scopes can be narrowed at mint: `bank:read`, `bank:write`, `tests:read`, `tests:write`, `results:read`, `results:write`, `send`. ## Delegate tokens (`adt_…`) For **someone else** — a colleague, a typing service, or our team doing assisted entry. Defaults: question-bank scopes only, 7-day expiry, 500 requests. Minting one gives you a ready-to-forward message; the delegate (or their agent) uses it exactly like an API token, and: - every question it adds is **stamped with the token** — you always know what came from whom; - it cannot see results, publish tests, or email students; - the first user binds it (`X-Delegate-Email` header identifies them); it stops at expiry, use-cap, or the moment you revoke it. ## Too busy to type questions? Mail your question paper to the address on your dashboard and our team will load it for you using exactly this mechanism — a 3-day delegate token minted against your account, every insertion attributable. --- # REST API The dashboard, the student player, and the MCP server all run on one API — there is no private surface. Machines authenticate with a [bearer token](/docs/tokens); the endpoints below are the useful subset. ## Question bank ``` GET /api/tests/bank?topic=&difficulty=&lang=&q=&limit=&offset= POST /api/tests/bank one question (schema: /docs/json-import) POST /api/tests/bank/import {"questions":[…]} batch, per-item report PUT /api/tests/bank/:id/translations/:lang ``` ## Tests ``` POST /api/tests/manage create draft POST /api/tests/manage/:id/sections {title?, marking, duration_minutes?} POST /api/tests/manage/:id/sections/:sid/questions {question_ids:[…]} or {pool:{count,…}} POST /api/tests/manage/:id/roster {rows:[{name,email?,phone?}]} POST /api/tests/manage/:id/publish {slot:"free"|"pack"|"pro"} GET /api/tests/manage/:id/links POST /api/tests/manage/:id/invite preview → …/invite/confirm queues mail ``` ## Results ``` GET /api/tests/results/:testId GET /api/tests/results/:testId/attempt/:attemptId PATCH /api/tests/results/:testId/attempt/:attemptId/answer {question_id, marks} POST /api/tests/results/:testId/publish GET /api/tests/results/:testId/export.csv ``` Errors are machine-readable (`{"error":"language_gaps","gaps":[…]}`), amounts are paise, times are ISO-8601 UTC, list endpoints paginate with `limit`/`offset`. The MCP tools are thin wrappers over these — anything the agent can do, plain HTTP can do. --- # Interactive game questions A question can be a small game: a simulation to explore, a puzzle to solve, a lab to experiment in. You paste ONE self-contained HTML file; we run it inside a double sandbox and record the score it reports. ## The security model (why you can trust pasted code) - The game is served with `Content-Security-Policy: sandbox allow-scripts; default-src 'none'` and embedded in `