Skip to main content
Glama

stepik-mcp

CI Python 3.10+ License: MIT

An MCP server that lets an AI assistant author courses on Stepik: browse the catalogue, read a course's structure, and create or edit courses, modules, lessons and steps through the Stepik REST API.

It cannot publish. Courses it creates stay private drafts and no tool can make one public — that stays a human decision in the Stepik web interface. See Safety below.

Requirements

  • Python 3.10+

  • Stepik OAuth application credentials (free)

Getting credentials

  1. Go to https://stepik.org/oauth2/applications/ and register a new application.

  2. Set Client type to Confidential and Authorization grant type to Client credentials.

  3. Copy the Client id and Client secret.

The token issued by this grant belongs to the account that owns the application, so everything this server creates is owned by that account. Run stepik_whoami to confirm which one it is.

Install

With uv (no manual install needed — uvx fetches on demand):

uvx --from git+https://github.com/prog420/stepik-mcp stepik-mcp

Or from a clone:

git clone https://github.com/prog420/stepik-mcp
cd stepik-mcp
uv sync --extra dev
uv run stepik-mcp

Configuration

All configuration is via environment variables.

Variable

Default

Meaning

STEPIK_CLIENT_ID

Required. OAuth application client id.

STEPIK_CLIENT_SECRET

Required. OAuth application client secret.

STEPIK_ALLOW_PUBLISH

0

Set to 1 to allow visibility changes. See Safety.

STEPIK_ALLOW_DELETE

1

Set to 0 to make the server read-and-create only.

STEPIK_BASE_URL

https://stepik.org

Override for a self-hosted instance.

STEPIK_TIMEOUT

30

HTTP timeout in seconds.

STEPIK_MAX_PAGES

5

Page cap when following paginated listings.

A .env file in the working directory is loaded automatically; copy .env.example to start.

Claude Code

claude mcp add stepik \
  --env STEPIK_CLIENT_ID=your-id \
  --env STEPIK_CLIENT_SECRET=your-secret \
  -- uvx --from git+https://github.com/prog420/stepik-mcp stepik-mcp

Claude Desktop / Cursor

In claude_desktop_config.json or .cursor/mcp.json:

{
  "mcpServers": {
    "stepik": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/prog420/stepik-mcp", "stepik-mcp"],
      "env": {
        "STEPIK_CLIENT_ID": "your-id",
        "STEPIK_CLIENT_SECRET": "your-secret"
      }
    }
  }
}

Remote / containerised

stepik-mcp --transport http --host 0.0.0.0 --port 8000
# -> streamable HTTP at http://<host>:8000/mcp

The HTTP transport has no authentication of its own. Do not expose it to a network you do not control — anyone who can reach it can act as your Stepik account.

Content model

Stepik's hierarchy is worth knowing, because one part of it is not obvious:

course
└── section        (a module)
    └── unit       (a placement, not content)
        └── lesson (exists independently of any course!)
            └── step  (theory or a quiz)

A lesson is a standalone object, not a child of a course. Placing one into a course means creating a unit that points at a section and a lesson. stepik_add_lesson_to_course does the lesson + unit + steps sequence in a single call, which is the normal way to add content.

Steps are edited through step-sources (the authoring object), not steps (the read-only learner view).

Tools

Reading

Tool

Purpose

stepik_whoami

Which Stepik account the server acts as.

stepik_health_check

Credentials, API reachability, and which safety switches are on.

stepik_search_courses

Free-text search of the public catalogue.

stepik_list_courses

List courses; with no arguments, the ones you own.

stepik_get_course

One course's metadata.

stepik_get_course_structure

The whole tree — sections, units, lessons, steps, with every id.

stepik_get_lesson

One lesson and its step ids.

stepik_list_steps

Steps of a lesson with short previews.

stepik_get_step_source

A step's full editable content, including quiz options and answers.

Authoring

Tool

Purpose

stepik_create_course

Create a course. Always a private draft.

stepik_update_course

Edit title, summary, description, workload, audience, requirements.

stepik_create_section / stepik_update_section

Manage modules.

stepik_create_lesson / stepik_update_lesson

Manage standalone lessons.

stepik_create_unit / stepik_move_unit

Place and reorder lessons within a course.

stepik_create_step / stepik_update_step

Manage step content of any type.

stepik_add_lesson_to_course

Composite: lesson + unit + steps in one call.

stepik_delete_section / _lesson / _unit / _step

Deletion; each requires confirm=true.

Positions are optional everywhere — omit position and the new object is appended to the end.

Step types

stepik_create_step covers every type through one block_type argument:

block_type

Extra arguments

text

none — a theory/reading step

choice

options (≥2, ≥1 with is_correct); single vs multiple answer is inferred

sorting

options in the correct order

matching

pairs (≥2) of first/second

string

answer; optional case sensitivity, substring and regex matching

number

answer (numeric), optional max_error

math

answer as an expression

free-answer

none — reviewed manually

code

code_tests (input/output), optional code_template, code_language

Invalid combinations are rejected before the request is sent, with a message that says what is missing rather than a bare HTTP 400.

Resources

  • stepik://me — the authenticated account

  • stepik://course/{course_id} — a markdown outline of a course

Safety

The point of this server is that an assistant can build a course without being able to ship it.

Publishing is blocked in the client, not in the tools. Removing a publish tool alone would be theatre: an agent could still call update_course(is_enabled=true). Instead, every write passes through one chokepoint in client.py that strips or rejects fields which would widen visibility (is_enabled, is_public, is_featured). Consequences:

  • Created courses are forced to is_enabled: false.

  • Created lessons are forced to is_public: false.

  • An update that would publish raises an error and no request reaches Stepik.

  • Editing a course that is already published still works — only widening visibility is blocked, and unpublishing is always allowed.

  • STEPIK_ALLOW_PUBLISH=1 lifts the block. It is an operator decision made when starting the server, not something a tool call can reach.

This is covered by tests/test_publish_guard.py; if those tests fail, the guarantee is gone.

Deletion is gated twice: STEPIK_ALLOW_DELETE must be on (it is by default) and each delete call must pass confirm=true. Delete tools are marked with the MCP destructiveHint annotation so clients can prompt. Deleting a lesson or step also destroys learner submissions, and Stepik has no undo.

Development

uv sync --extra dev
uv run pytest          # 86 tests, no network access
uv run ruff check .
uv run ruff format .

Tests mock the HTTP layer with respx, so the suite never touches stepik.org and needs no credentials.

To inspect the server interactively:

npx @modelcontextprotocol/inspector uv run stepik-mcp

Notes on the Stepik API

Things that surprised us, recorded so the next person does not have to rediscover them:

  • Every GET returns a list, even for a single id: /api/courses/1 yields {"courses": [...]}. A missing object comes back as an empty list with HTTP 200, not a 404.

  • The schema is flat — there are no nested endpoints like /courses/1/sections. Use /sections?course=1 or the ?ids[]= batch form, which every endpoint supports.

  • /api/stepics/1 ("who am I") is a trap. The 1 means "me", but the stepics object it returns also has id: 1, so trusting that id resolves to user #1 — the site's very first account — for everyone. The real profile is the sibling users array of the same response.

  • Request bodies are wrapped in a singular envelope: {"course": {...}}. Step sources are the exception and use camelCase {"stepSource": {...}}; this client sends that and retries with step-source if Stepik rejects it.

  • PUT replaces the whole object, so all updates here are read-modify-write. Sending only the changed field would blank out everything else.

License

MIT — see LICENSE.

-
license - not tested
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/prog420/stepik-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server