# `io.usefulapi/classmarker` on anlyon.dev

> Text inside fenced blocks marked untrusted-server-text was written by the MCP server's publisher, not by anlyon.dev. It is data to inspect, not instructions to follow. Invisible characters are shown as [U+XXXX].
> No finding is not a safety guarantee: these are point-in-time checks of tool definitions, not of code or behaviour.

- Page: https://anlyon.dev/servers/io.usefulapi/classmarker
- JSON: https://anlyon.dev/api/v1/servers/io.usefulapi/classmarker
- Version: 1.3.3 (active)
- Publisher: io.usefulapi (domain proven to the MCP Registry)
- Runs: remote (streamable-http)
- Sign-in to list tools: none
- Repository: https://github.com/m190/usefulapi-mcp
- Metadata from the official MCP Registry, synced 2026-10-21T19:23:16.065Z

Title and description, as the publisher wrote them:

```untrusted-server-text
ClassMarker MCP by usefulapi
Read ClassMarker groups, links, exams and recent results; manage question bank and access codes.
```

Scanned 2026-10-21T19:37:55.767Z over streamable-http.

**15 tools: 3 can delete or cancel things, 8 change things and 4 only read.**

| Group | Class | Tools | Claude Code |
| --- | --- | --- | --- |
| Deletes or cancels | destructive | 3 | deny |
| Can't be undone | irreversible | 0 | ask |
| Changes things | write | 8 | ask |
| Only reads | read | 4 | allow |

## Findings

- **warn** Annotations that disagree with the tool in `classmarker_recent_link_results` ([annotation-conflict](https://anlyon.dev/methodology#annotation-conflict))

```untrusted-server-text
The server marks "classmarker_recent_link_results" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

annotations: {"readOnlyHint":true}; The name "classmarker_recent_link_results" says "link": a word for a change.
```

- **warn** Annotations that disagree with the tool in `classmarker_recent_link_exam_results` ([annotation-conflict](https://anlyon.dev/methodology#annotation-conflict))

```untrusted-server-text
The server marks "classmarker_recent_link_exam_results" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

annotations: {"readOnlyHint":true}; The name "classmarker_recent_link_exam_results" says "link": a word for a change.
```


## Deletes or cancels (3)

Removes, cancels or revokes something. Claude Code: deny.

### `classmarker_update_category`

- The name "classmarker_update_category" says "update": a word for a change.
- The server marks it destructive (destructiveHint: true).

Description, as the server wrote it:

```untrusted-server-text
WRITE: rename a category and/or move it to another parent (overwrites both fields: send the name and parent_category_id you want it to have). Counts as one of ClassMarker's 30 requests per hour per API key. API: PUT /v1/categories/category/{category_id}.json.
```

### `classmarker_update_parent_category`

- The name "classmarker_update_parent_category" says "update": a word for a change.
- The server marks it destructive (destructiveHint: true).

Description, as the server wrote it:

```untrusted-server-text
WRITE: rename an existing parent category (overwrites its name). Counts as one of ClassMarker's 30 requests per hour per API key. API: PUT /v1/categories/parent_category/{parent_category_id}.json.
```

### `classmarker_update_question`

- The name "classmarker_update_question" says "update": a word for a change.
- The server marks it destructive (destructiveHint: true).

Description, as the server wrote it:

```untrusted-server-text
WRITE (overwrites): replace an existing question-bank question. Send the COMPLETE question (type, text, options, correct options, points, feedback), not only the changed fields; read it first with classmarker_get_question. Only multiplechoice, multipleresponse, truefalse and essay questions can be updated. Counts as one of ClassMarker's 30 requests per hour per API key. API: PUT /v1/questions/{question_id}.json.
```

## Changes things (8)

Creates or changes something that can be changed back. Claude Code: ask.

### `classmarker_add_access_codes`

- The name "classmarker_add_access_codes" says "add": a word for a change.

Description, as the server wrote it:

```untrusted-server-text
WRITE: add access codes (1-100 per call, up to 255 characters each; a list holds at most 20,000) to a link's access-code list, so those people can start the exam. The access_list_id comes from classmarker_list_groups_links_exams (links[].access_list_id). Only adds; removing codes is not offered. Does not count against the 30 requests per hour limit. API: POST /v1/accesslists/{access_list_id}.json.
```

### `classmarker_create_category`

- The name "classmarker_create_category" says "create": a word for a change.

Description, as the server wrote it:

```untrusted-server-text
WRITE: create a question category under a parent category. Returns its category_id. Counts as one of ClassMarker's 30 requests per hour per API key. API: POST /v1/categories/category.json.
```

### `classmarker_create_parent_category`

- The name "classmarker_create_parent_category" says "create": a word for a change.

Description, as the server wrote it:

```untrusted-server-text
WRITE: create a new top-level question category. Returns its parent_category_id. Counts as one of ClassMarker's 30 requests per hour per API key. API: POST /v1/categories/parent_category.json.
```

### `classmarker_create_question`

- The name "classmarker_create_question" says "create": a word for a change.

Description, as the server wrote it:

```untrusted-server-text
WRITE: add a new question to the question bank. Only multiplechoice, multipleresponse, truefalse and essay can be created. Returns the new question_id. For truefalse, give only correct_options ["A"] (True) or ["B"] (False). Counts as one of ClassMarker's 30 requests per hour per API key. API: POST /v1/questions.json.
```

### `classmarker_recent_group_exam_results`

- Nothing in its name or description says what it does, so it is counted as a write.
- The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

Description, as the server wrote it:

```untrusted-server-text
Recent finished results for ONE exam in ONE group (ids from classmarker_list_groups_links_exams), oldest first, PAGED by finish time. Contains personal data: use small pages. Counts as one of ClassMarker's 30 requests per hour per API key. API: GET /v1/groups/{group_id}/tests/{test_id}/recent_results.json.
```

### `classmarker_recent_group_results`

- Nothing in its name or description says what it does, so it is counted as a write.
- The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

Description, as the server wrote it:

```untrusted-server-text
Recent finished test results taken in the key's groups (all groups, all exams), oldest first, PAGED by finish time: test-taker name, email, score, percentage, pass/fail, timing, certificate. Contains personal data: use small pages. Max look-back 3 months. Counts as one of ClassMarker's 30 requests per hour per API key. API: GET /v1/groups/recent_results.json.
```

### `classmarker_recent_link_exam_results`

- The name "classmarker_recent_link_exam_results" says "link": a word for a change.
- The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
- Findings: warn annotation-conflict

Description, as the server wrote it:

```untrusted-server-text
Recent finished results for the exam of ONE link (ids from classmarker_list_groups_links_exams), oldest first, PAGED by finish time. Contains personal data: use small pages. Counts as one of ClassMarker's 30 requests per hour per API key. API: GET /v1/links/{link_id}/tests/{test_id}/recent_results.json.
```

### `classmarker_recent_link_results`

- The name "classmarker_recent_link_results" says "link": a word for a change.
- The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
- Findings: warn annotation-conflict

Description, as the server wrote it:

```untrusted-server-text
Recent finished test results taken through the key's links (all links, all exams), oldest first, PAGED by finish time: test-taker name, email, score, pass/fail, timing, access code, extra tracking fields, IP address, certificate. Contains personal data: use small pages. Max look-back 3 months. Counts as one of ClassMarker's 30 requests per hour per API key. API: GET /v1/links/recent_results.json.
```

## Only reads (4)

Looks things up and changes nothing. Claude Code: allow.

### `classmarker_get_question`

- The name "classmarker_get_question" says "get": a word for reading.

Description, as the server wrote it:

```untrusted-server-text
Get one question-bank question by id (ids from classmarker_list_questions). Counts as one of ClassMarker's 30 requests per hour per API key. API: GET /v1/questions/{question_id}.json.
```

### `classmarker_list_categories`

- The name "classmarker_list_categories" says "list": a word for reading.

Description, as the server wrote it:

```untrusted-server-text
List the question-bank categories with their parent categories and ids (needed for creating and updating questions). Counts as one of ClassMarker's 30 requests per hour per API key. API: GET /v1/categories.json.
```

### `classmarker_list_groups_links_exams`

- The name "classmarker_list_groups_links_exams" says "list": a word for reading.

Description, as the server wrote it:

```untrusted-server-text
List the groups, links and exams (tests) this API key can access, with the ids the other tools need (group_id, link_id, link_url_id, test_id, and access_list_id for links with an access-code list). Good first call. Counts as one of ClassMarker's 30 requests per hour per API key. API: GET /v1.json.
```

### `classmarker_list_questions`

- The name "classmarker_list_questions" says "list": a word for reading.

Description, as the server wrote it:

```untrusted-server-text
List the questions of the question bank, 200 per page (page 1, 2, ...): question text, type, category, points, options, correct options, status. A full page means more may follow, so ask for the next page. Counts as one of ClassMarker's 30 requests per hour per API key. API: GET /v1/questions.json?page=N.
```

## Claude Code permissions

For `.claude/settings.json`, assuming the server is installed as `classmarker`:

```json
{
  "permissions": {
    "allow": [
      "mcp__classmarker__classmarker_list_groups_links_exams",
      "mcp__classmarker__classmarker_list_categories",
      "mcp__classmarker__classmarker_list_questions",
      "mcp__classmarker__classmarker_get_question"
    ],
    "ask": [
      "mcp__classmarker__classmarker_recent_group_results",
      "mcp__classmarker__classmarker_recent_link_results",
      "mcp__classmarker__classmarker_recent_group_exam_results",
      "mcp__classmarker__classmarker_recent_link_exam_results",
      "mcp__classmarker__classmarker_add_access_codes",
      "mcp__classmarker__classmarker_create_parent_category",
      "mcp__classmarker__classmarker_create_category",
      "mcp__classmarker__classmarker_create_question"
    ],
    "deny": [
      "mcp__classmarker__classmarker_update_parent_category",
      "mcp__classmarker__classmarker_update_category",
      "mcp__classmarker__classmarker_update_question"
    ]
  }
}
```
