{
  "markdown": "# Reach120 MCP server\n\nAn [MCP](https://modelcontextprotocol.io) server that gives an AI agent a governed\npath to the [Reach120](https://www.reach120.com) TOEFL iBT practice API: fetch\nWriting practice prompts, and score a learner's written response.\n\nReach120 is an independent practice platform. It is not affiliated with, endorsed\nby, or administered by ETS. TOEFL® is a registered trademark of ETS. Reach120 is\nnot affiliated with, endorsed by, or approved by ETS.\n\n## Tools\n\n| Tool | What it does | Cost |\n|---|---|---|\n| `list_practice_prompts` | List Writing practice prompts, with the id used to score against one | Reads stored rows, runs no model |\n| `get_practice_prompt` | Fetch one prompt with its full scenario or discussion context | Reads a stored row, runs no model |\n| `score_writing_response` | Score a written response, returning an automated practice score and a short rationale | Runs the scorer; metered against your key |\n\n## You bring the key\n\nThis server carries no credential. It forwards the key you place in\n`REACH120_API_KEY` and returns the API's answer unchanged.\n\nThat is a design decision, not an omission. Every limit that matters lives on the\nserver side of the API and is evaluated on every call — a per-key request window,\na per-key daily scoring allowance, and a service-wide spending gate. A client that\nenforced its own limits would only be enforcing them on people who chose to keep\nusing the client, so this one enforces none and cannot be a way around any of\nthem. Delete this package and call the endpoint directly and you get identical\nrefusals.\n\nThere is no unauthenticated scoring path. An agent with no key receives a refusal\nnaming where a key comes from, and nothing is sent.\n\nRequest a key and read the full limit, error-code and version policy at\n**<https://www.reach120.com/developers>**.\n\n## Install\n\n**This package is not published on npm.** The install path is this repository, and\nit is the only one — `@reach120/mcp-server` does not resolve on the public npm\nregistry and no other package name stands in for it.\n\n```bash\nnpx -y github:reach120/reach120-mcp\n```\n\nnpm compiles the TypeScript during that install through the `prepare` script, so\nthere is no separate build step.\n\nOr clone it and run it from source:\n\n```bash\ngit clone https://github.com/reach120/reach120-mcp.git\ncd reach120-mcp\nnpm install\nnpm run build\nnode dist/index.js\n```\n\n## Configure\n\nClaude Desktop (`claude_desktop_config.json`), Claude Code, or any MCP client that\nspeaks stdio:\n\n```json\n{\n  \"mcpServers\": {\n    \"reach120\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"github:reach120/reach120-mcp\"],\n      \"env\": {\n        \"REACH120_API_KEY\": \"r120_live_your_key_here\"\n      }\n    }\n  }\n}\n```\n\n| Variable | Required | Default | Meaning |\n|---|---|---|---|\n| `REACH120_API_KEY` | yes | — | Your Reach120 API key. Tools refuse without it. |\n| `REACH120_API_BASE_URL` | no | `https://www.reach120.com` | Point at another deployment. |\n| `REACH120_API_TIMEOUT_MS` | no | see `readConfig` in `src/api.ts` | Per-request timeout, in milliseconds. |\n\n## What a refusal looks like\n\nLimits refuse with a machine-readable code rather than degrading. A call that hits\nthe key's daily allowance is not quietly scored on a cheaper engine and handed back\nas if it were the engine you asked for — the code names what happened, and the\nerror table on `/developers` says what each one means.\n\n## Verify it locally\n\n```bash\nnpm install\nnpm run build\nnpm run verify\n```\n\n`npm run verify` starts the built server over stdio, completes a real MCP\nhandshake with the SDK client, prints the advertised tools, and calls one tool\nwithout a key to show the refusal path.\n\n## Scores are practice feedback\n\n`score_writing_response` returns automated practice feedback produced by Reach120.\n\nIt is not an ETS score and is not reported on the official scale. This README\ndeliberately does not name the scale the number *is* on: that fact is published\nbeside a mandatory limitation the claims ledger requires, which a package page\ncannot render. The response shape is documented on\n**<https://www.reach120.com/developers>**.\n\nTOEFL® is a registered trademark of ETS. Reach120 is not affiliated with, endorsed\nby, or approved by ETS.\n\n## Licence\n\nMIT. See [LICENSE](./LICENSE).\n",
  "bytes": 4283,
  "sha": "180b95762188fd6af29d4119004645148f3f6ed2001596eec7e239e43d1a6ee5",
  "repo_slug": "reach120/reach120-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_reach120_reach120_mcp_1c9b2dae/readme"
}