stepik-mcp
An MCP server that lets AI assistants author courses on Stepik: browse the catalogue, read course structure, create or edit courses, modules, lessons and steps through the Stepik REST API, while preventing publishing.
README
stepik-mcp
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
- Go to https://stepik.org/oauth2/applications/ and register a new application.
- Set Client type to
Confidentialand Authorization grant type toClient credentials. - 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 accountstepik://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=1lifts 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/1yields{"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=1or the?ids[]=batch form, which every endpoint supports. /api/stepics/1("who am I") is a trap. The1means "me", but thestepicsobject it returns also hasid: 1, so trusting that id resolves to user #1 — the site's very first account — for everyone. The real profile is the siblingusersarray 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 withstep-sourceif Stepik rejects it. PUTreplaces 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.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。