Repository navigation
MCP Server en
External agents (Claude Code, Cursor, Claude Desktop, Openclaw and others) can call this system's notebooks, questions, knowledge tags and review data through Model Context Protocol over Streamable HTTP.
Endpoint: POST /api/mcp. It is stateless: every request returns a JSON response and no Mcp-Session-Id is kept; GET returns 405 because there is no standalone SSE stream. The target account is fixed, and every tool reads or writes only that account's data.
External agent → POST /api/mcp (Bearer or x-api-key)
→ MCP tools (list_questions / add_question, etc.)
→ this system's database (fixed account data)
MCP_API_KEY=replace-with-a-random-integration-key
MCP_USER_EMAIL=student@example.com-
MCP_API_KEY: the integration key callers send. Generate it withopenssl rand -hex 32. -
MCP_USER_EMAIL: the fixed target account as an email (a username also works); the account must exist and be enabled.
The endpoint is only available when both are non-empty; otherwise it returns 503. Restart or recreate the service after changing environment variables. One endpoint maps to one account; for multiple accounts, deploy one instance per account or use separate data directories.
Either request header works:
-
Authorization: Bearer <MCP_API_KEY>(recommended) x-api-key: <MCP_API_KEY>
The key is compared in constant time. A wrong key or a missing/disabled account returns 401. The key travels in request headers, so use HTTPS across networks and keep it out of logs.
Claude Code:
claude mcp add --transport http wrong-notebook http://localhost:3000/api/mcp \
--header "Authorization: Bearer ${MCP_API_KEY}"For other Streamable HTTP clients (Claude Desktop, Cursor and similar), fill in the URL and headers in their own format, for example:
{
"mcpServers": {
"wrong-notebook": {
"type": "http",
"url": "http://localhost:3000/api/mcp",
"headers": { "Authorization": "Bearer replace-with-MCP_API_KEY" }
}
}
}Field names vary by client (for example streamable-http); the URL and headers are what matter.
Initialize:
curl --fail-with-body http://localhost:3000/api/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H "Authorization: Bearer ${MCP_API_KEY}" \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'Change method to tools/list to list tools. Call a tool:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "add_question",
"arguments": {
"notebookId": "notebook-id",
"questionText": "Solve x+1=2",
"answerText": "x=1",
"analysis": "Move the constant term",
"knowledgePoints": ["Linear equation in one variable"],
"gradeSemester": "七年级上",
"difficultyStars": 2
}
}
}Accept may be omitted; the server fills it in. Content-Type must be application/json. The request body limit is 24 MiB, enough for add_question with a Base64 image.
| Tool | Purpose | Main arguments |
|---|---|---|
list_notebooks |
List notebooks with question counts | none |
create_notebook |
Create a notebook |
name, stage, subjectKey, tagSet
|
list_questions |
Filter, search and paginate questions |
notebookId, query, tag, chapter, mastery, timeRange, gradeSemester, paperLevel, questionTypes, page, pageSize
|
get_question |
Read one question's text, answer, analysis, tags and mastery status |
id (CUID or 6-character short ID), includeImage
|
add_question |
Add a question; unknown knowledge points are created in the tag set | question text/image, knowledgePoints, notebookId or subjectName, gradeSemester, difficulty and type |
list_tags |
List knowledge tags |
subject, tagSet, query, flat (leaf list by default) |
list_due_reviews |
Questions due for review today |
notebookId, page, pageSize
|
get_weak_tags |
Rank weak knowledge points |
notebookId, range (7d/30d/6m/all), limit
|
Notes:
- Tool results are JSON text; clients parse the JSON. Failures return
isError: true. -
add_questionfollows the same duplicate detection, grade and paper-group rules as the Add Question API. A duplicate returnsduplicateand records another wrong attempt. WhengradeSemesteris omitted and the account uses AI grade recognition, one extra AI call counts against the daily quota; if the account has not accepted AI data consent, AI-dependent calls fail. -
get_questionomits the original image by default;includeImage: truemay return a large Base64 data URL. -
list_questionsreturns compact card fields (question text, tags, mastery status) without answers, animations or large images;pageSizeis capped at 1000. -
list_tagsreturns leaf knowledge-point names by default, ready foradd_questionknowledgePoints.
Implementation: MCP route, tool registration, data access.