@techprofessor/mcp
v0.7.0
Published
MCP server to create, read, and update TechProfessor artifacts (quizzes, tours, flashcards, playgrounds, exams, syllabi), courses, and code challenges from any MCP client — you author the content, this persists it.
Readme
TechProfessor MCP server
A push-based CRUD surface over TechProfessor artifacts (quizzes, tours, flashcards, playgrounds, exams, syllabi, showcases) for any MCP client (Claude Desktop, Cursor, other agents). This server never generates content — you (the client) author the artifact, and the MCP persists, reads, and updates it. Create and Update just push what you made: no server-side AI, no provider key, no serverless time limit.
The flow for every type is the same: call get_artifact_schema for the exact
shape, author the artifact, push_artifact to save it, then read it back with
get_artifact / list_artifacts and edit it with update_artifact. Optionally
group existing artifacts into a course with the course tools.
Tools
| Tool | What it does |
|------|--------------|
| push_artifact | Create / full-replace — upload one artifact you authored, or pass artifact_id (the API session_id) to rewrite an existing one in place. Always set audience + level (ask if unspecified; defaults technical + mixed). |
| push_artifacts | Create (bulk) — upload authored artifact JSON files (a directory or single file). Local package only. |
| get_artifact | Read — fetch one artifact in full. Tours return narratives[].groups[].snippets[]; other types return items[]. |
| list_artifacts | Read (list) — list your artifacts and their artifact_id (the API session_id). Filter by type, team_id, repo_id, q, source_ref, or lifecycle (active | archived | all, default active). |
| update_artifact | Update — edit an artifact in place: title/description/audience/level and/or content rows by id. |
| archive_artifact | Hide / restore — take an artifact out of the default listing, or put it back. Reversible; changes no content. Prefer this over deleting. |
| delete_artifact | Delete (permanent) — remove an artifact for good. Also drops it from any course referencing it. |
| get_artifact_schema | Return the exact package shape (+ example) for a type, so an authored artifact validates on the first upload. |
| list_repos | List your repositories and their repo_id (to attach a pushed artifact to a repo). |
| list_teams | List your teams and their team_id (target for pushes). |
| list_folders | List folders inside a repo (folder_id for move/create). |
| create_folder | Create a folder in a repo (optional parent_id to nest). |
| rename_folder | Rename a folder or update its description. |
| move_artifact | Move an artifact into a folder, or pass folder_id: null to unfile it. |
| list_courses | List your courses and their course_id. |
| create_course | Create a new (empty) course. |
| get_course | Fetch one course's items, each with the artifact it points to. |
| update_course | Update a course's title, description, and/or status (active | archived). |
| add_course_item | Add an existing artifact (by artifact_id, the API session_id) as a step in a course. |
| remove_course_item | Remove a step by its course item_id (from get_course). The artifact it pointed at is not deleted. |
| reorder_course_items | Reorder every step — pass the full permutation of item ids. |
| delete_course | Delete (permanent) — remove a course and its steps. The artifacts the steps pointed at are not deleted. |
| list_code_challenges | List the code challenges you authored and their challenge_id. Templates only. |
| create_code_challenge | Create a code challenge — a brief plus ordered tasks, each graded by its own checks. |
| get_code_challenge | Fetch one challenge in full: brief, workspace file, and every task with its checks and reference solution. |
| update_code_challenge | Edit the brief and/or patch existing tasks (by task_id or 0-based sort_order). |
Create an artifact (push)
You author the artifact; this server persists it. Steps:
get_artifact_schema({ type })— get the exact package shape (+ an example) forquiz/tour/flashcards/playground/exam/syllabus/showcase.- Author the package JSON yourself (one artifact per package).
push_artifact({ artifact, repo_id?, team_id?, artifact_id? })— save it.repo_idattaches it to a repo (fromlist_repos); omit for a standalone artifact.team_id(fromlist_teams) saves to a team space instead of your private one. Passartifact_id(the APIsession_id, fromlist_artifacts) to full-replace a known artifact in place — including empty UI/generate tours.source_refalone upserts only prior ingest/MCP packages that own that key (not generate-created sessions).
push_artifacts does the same from .json files — a directory or a single
file. The response includes a web_link you can open to study or share.
The link is placement-aware: root artifacts use their flat type home, while
repo-backed artifacts use the nested repository/view home. All links returned
by push, list, get, and update are canonical browser links.
To read and patch existing artifacts, follow the flow list_artifacts →
get_artifact → update_artifact. Call list_artifacts to find an artifact_id,
then get_artifact to fetch the current content and ids. Non-Tours retain the
flat items[] update shape. Empty Tour groups need push_artifact with
artifact_id (full replace), not update_artifact. A Tour has no parallel
top-level items: read and patch it through narratives[]: set Narrative
diagram (Mermaid), references (source links), concepts (Concept slugs),
and/or groups[].snippets[] with the existing
Narrative/Group ids for containment and only the changed Snippet fields.
Group patches may also set description and diagram. Group title and
importance stay read-only. Snippet why is Markdown. Also copy the Tour's tour_revision into
expected_revision; if the update conflicts, reload the Tour before
retrying. For example:
{
"artifact_id": "...",
"expected_revision": 4,
"narratives": [{
"id": "narrative-id",
"diagram": "flowchart TD\n A --> B",
"groups": [{
"id": "group-id",
"snippets": [{ "id": "snippet-id", "why": "Updated explanation" }]
}]
}]
}Edits happen in place (no new version is created) and are strictly owner-scoped — you only ever see or edit artifacts you own.
push_artifacts takes a path (a directory of artifact JSON files or a single
.json file; defaults to .techprof) and an optional team_id. It POSTs each
file to the ingest endpoint and reports one line per file. The server caps
ingests at 30/min; larger batches may be rate-limited (429).
Exams and syllabi
push_artifact/push_artifacts also accept type: "exam" and
type: "syllabus" — call get_artifact_schema({ type: "exam" }) or
get_artifact_schema({ type: "syllabus" }) for the exact shape before
authoring one:
- An exam's
items[]interleaves questions and coding exercises in one ordered list, each keyed bykind: "question" | "playground". Exam questions supportmultiple_choice/freeformonly — nofill_blank. - A syllabus's
materials[]holdshtml/mermaidcontent — the field isbody(notcontent). PDF materials are upload-only (not supported viapush_artifact).
Both are editable with update_artifact the same way as any other artifact:
get_artifact returns each exam item's kind and each syllabus material's
fields, and you patch them by id (an exam question edits like a quiz
question, an exam playground edits like a playground spec, a syllabus
material edits title/body/sort_order).
Showcase
A showcase is marketing/positioning copy, not a learning artifact — one
shared brief re-told as audience-shaped content. push_artifact/
push_artifacts accept type: "showcase"; call
get_artifact_schema({ type: "showcase" }) for the exact shape before
authoring one.
showcase_formatis"explainer"(cold web visitor, landing-page style),"one_pager"(customer/partner/press, dense one-page sheet), or"posts"(followers/subscribers, ready-to-post blog + email copy)."pitch_deck"is a real format value but Phase 2 (slide model +.pptxexport) — not yet pushable.sections[]holds the content, each{ kind, heading, body }in order.kindis per-format convention (not schema-enforced): explainer useshero | problem | how | benefits | faq | cta; one_pager usesheader | problem | what | benefit | who | proof | cta; posts usesblog | email.- Either inline a new
brief(one_liner,who_for,problem,how_it_works,why_different,proof,business_model,team,cta,tone) or link an existing one you own withbrief_id— the two are mutually exclusive.
Like exam/syllabus, showcase is author-only (no generate_showcase tool).
update_artifact edits a section's heading/body/sort_order by id; its
kind remains read-only.
Courses
A course groups existing artifacts (by artifact_id, the API session_id) into an ordered
sequence — it is not an artifact type itself and is never pushed via
push_artifact. Typical flow: push the pieces you want (a tour, an exam, a
syllabus, …) with push_artifact, create_course to make the container, then
add_course_item once per piece with its artifact_id. Use list_courses /
get_course to review what's already in a course; update_course,
remove_course_item, and reorder_course_items to maintain it.
Code challenges
A code challenge is a guided build: one brief, one workspace file, and an
ordered set of tasks that each narrow the goal and are graded by their own
acceptance checks. Learners fork a challenge; their copy carries their own code
buffer and progress. It is not an artifact type — push_artifact does not
create one.
Author with create_code_challenge (brief + ordered tasks). Read one back with
get_code_challenge before editing, then update_code_challenge to change the
brief or patch a task. A task patch identifies its target by task_id or the
0-based sort_order, and carries only the fields to change; tests replaces
that task's checks wholesale. Tasks cannot be added, removed or reordered
through the API — that stays in the challenge editor.
Two things to know:
get_code_challengereturns the gated reference solutions. It is owner-only for that reason. Never paste a solution to someone working the challenge.- Every acceptance check needs both a
nameand acode. A check with a blankcodewould grade as an automatic pass, so it is rejected rather than saved.
Setup
- In TechProfessor, open Settings → Developer & MCP and create a token
(
tp_pat_…). Copy it — it is shown only once. No AI provider key is needed — this server never generates content, it only stores what you author. - Add the server to your MCP client (below). Prefer the hosted HTTP endpoint
(
https://tech-professor.com/api/mcp) — no clone or build needed.
Configure your MCP client
Hosted HTTP (recommended)
No local Node/npx required. Point any MCP client at the hosted Streamable HTTP
endpoint and pass your token as a Bearer header — same shape as GitHub Copilot's
remote MCP. The endpoint speaks MCP protocol 2026-07-28 (stateless) and
still serves 2025-era clients:
claude mcp add-json techprofessor \
'{"type":"http","url":"https://tech-professor.com/api/mcp","headers":{"Authorization":"Bearer tp_pat_xxx"}}'claude mcp add --transport http techprofessor https://tech-professor.com/api/mcp \
--header "Authorization: Bearer tp_pat_xxx"Cursor / Claude Desktop / .mcp.json:
{
"mcpServers": {
"techprofessor": {
"type": "http",
"url": "https://tech-professor.com/api/mcp",
"headers": { "Authorization": "Bearer tp_pat_xxx" }
}
}
}Settings → Developer & MCP also shows a ready-made add-json command with
your freshly created token filled in.
Local stdio (npx) — optional
Prefer HTTP above. Use the published package when you need filesystem
push_artifacts (bulk upload from a directory):
claude mcp add techprofessor -s user \
-e TECHPROF_API_TOKEN=tp_pat_xxx \
-- npx -y @techprofessor/mcpclaude mcp add-json techprofessor \
'{"type":"stdio","command":"npx","args":["-y","@techprofessor/mcp"],"env":{"TECHPROF_API_TOKEN":"tp_pat_xxx"}}'-s user registers it for every project; drop it to add only the current one.
TECHPROF_BASE_URL defaults to https://tech-professor.com (the hosted app);
set it only to point at your own deployment.
