# scorm-mcp-server > Turn self-contained HTML, **a Claude Design `.dc` bundle** or **a mobile-learning platform content export** (Excel activity templates + media) into a **SCORM 2004 (or 1.2)** package ready to import into any LMS — assets inlined for **100% offline**, completion / progress / **score** tracking injected, ADL schemas bundled. [![Try it online](https://img.shields.io/badge/demo-try%20online-0066cc)](https://scormpackager.vercel.app) [![giacomomaria81/scorm-mcp-server MCP server](https://glama.ai/mcp/servers/giacomomaria81/scorm-mcp-server/badges/score.svg)](https://glama.ai/mcp/servers/giacomomaria81/scorm-mcp-server) [![npm](https://img.shields.io/npm/v/scorm-mcp-server?logo=npm&color=cb3837)](https://www.npmjs.com/package/scorm-mcp-server) ![License: MIT](https://img.shields.io/badge/License-MIT-green.svg) ![Node](https://img.shields.io/badge/node-%E2%89%A520-339933?logo=node.js&logoColor=white) ![SCORM](https://img.shields.io/badge/SCORM-2004%20%2B%201.2-0a66c2) ![MCP](https://img.shields.io/badge/MCP-server-6E56CF) ![Tests](https://img.shields.io/badge/tests-325%2F325%20passing-brightgreen) ![Validated](https://img.shields.io/badge/SCORM%20Cloud-validated-success) ![SCORM test harness demo](./assets/demo.gif) *The bundled local harness (`scorm-test-harness.html`) playing a package: progress 0 → 100%, completion, and the live LMS API-call log (0 errors). Illustration.* An **MCP server** exposing three tools: **`scorm_package`** converts a finished HTML learning module into a `.zip` (PIF) any SCORM-compliant LMS can import, **`scorm_validate`** checks any existing SCORM zip (made by any tool) and explains exactly why an LMS would reject it, and **`scorm_selftest`** is a 1-second health check. **Principle: WRAP, don't rewrite.** Your HTML is preserved; the tool only: 1. **Inlines every asset** (CSS, `@import`, fonts, JS, images, `srcset`, favicons) as data URIs → runs **100% offline**. 2. **Injects a small runtime** that reports **completion**, **progress (%)** and **time spent**, with **resume** across sessions. 3. **Generates the manifest** and **bundles the 15 official ADL XSD schemas** — the manifest is validated against them (real conformance, not just "well-formed"). ## ✅ Status — validated on a real LMS - **325/325 automated checks** green: 23 converter · 15 runtime · 15 MCP · 1 schema conformance (`xmllint`) · 6 security · 11 features · 13 auto-milestones · 21 V2 (bundle / `.dc` / score) · 10 output-dir · 9 tracking-signal · 32 hardening · 29 SCORM 1.2 · 12 CLI/batch · 16 web UI · **44 mobile-learning migration** · **35 package validation** · **33 question-level interactions** — plus 6 bonus strict-runtime checks (`scorm-again`). - **SCORM Cloud (real LMS):** imports cleanly (recognized as *SCORM 2004 4th Ed.*, "manifest looks great"), and the dashboard reports **completion = complete, success = passed, time tracked**. ## Input formats | Input (`input_path` or `html`) | Handling | |---|---| | A single self-contained `.html` (e.g. Claude Design "standalone HTML" export) | assets inlined, runtime injected — v1 path | | A **folder or `.zip`** (multi-file module) | whole tree preserved; entry HTML inlined; manifest lists every file | | A **Claude Design `.dc` bundle** (`*.dc.html` + `support.js` + `_ds/`) | auto-detected; CDN libs (React/Babel…) **vendored offline** via `window.__resources` (no source patch); runtime injected before `support.js` | | A **mobile-learning platform content export** (Excel activity templates + `media/`) | auto-detected; an interactive HTML course is **rebuilt from the templates** — info / transition / flash cards, quiz questions, media codes (`[media:…]`, `[H1:…]`, `[quote:…]`, `!!`), scored quizzes reporting `cmi.score` — then packaged. Course title derived from the template names; with `--batch`, a whole catalogue migrates in one run | Pass a `.dc` bundle as its **folder or `.zip`** (not the lone `.dc.html`, which is inert without its siblings). ## Scores & quizzes (optional) Set **`mastery_score`** (0..1) to enable score-based success and add sequencing objectives to the manifest. Report the score from your content in one line — no SCORM knowledge required: ```js window.SCORM2004.score(8, 0, 10); // raw, min, max window.dispatchEvent(new CustomEvent("scorm:score", { detail: { raw: 8, min: 0, max: 10 } })); window.dispatchEvent(new CustomEvent("scorm:progress", { detail: 0.5 })); // 0..1 window.dispatchEvent(new CustomEvent("scorm:complete")); ``` The runtime maps these to `cmi.score.*`, sets `success_status = passed/failed` against `mastery_score`, and reports completion/progress. (`dc:*` event names are accepted as aliases.) **Question-level tracking (v2.3)** — report each answer as a `cmi.interactions` record, so the LMS gradebook shows *which* questions were missed, not just the total: ```js window.SCORM2004.interaction({ id: "quiz1-q3", type: "choice", description: "Which colour is the brand?", learnerResponse: "Blue", correctResponse: "Red", result: false, latencyMs: 12000, }); // or, without touching the API: window.dispatchEvent(new CustomEvent("scorm:interaction", { detail: { id: "q3", result: true } })); ``` Dialect-aware (2004 `learner_response`/`timestamp` vs 1.2 `student_response`/`time`, `incorrect` vs `wrong`) and best-effort by design: an LMS that refuses interaction writes gets a logged warning and the session carries on. Quizzes generated by the mobile-learning migration report their interactions automatically — one record per question, with the question text, the learner's answer, the expected answer and the latency. ## SCORM 1.2, batch mode, CLI (v2.1) **SCORM 1.2** — pass `scorm_version: "1.2"` and you get a 1.2 manifest (validated against the bundled 1.2 XSDs, with `adlcp:masteryscore` when `mastery_score` is set). The injected runtime is *adaptive*: it speaks to whichever API the hosting LMS exposes (`API_1484_11` or `API`), maps the data model (single `lesson_status`, 0-100 score, `HH:MM:SS` session time, 4096-char suspend data) and never downgrades a `passed` status. **Batch** — `batch: true` treats `input_path` as a directory of courses (each sub-directory, `.zip` or `.html` = one course). One package per course, one consolidated `batch-report.json`, and a broken course never sinks the others. **CLI** — no MCP client required: ```bash npx -y scorm-mcp-server ui # local drag & drop web UI npx -y scorm-mcp-server pack course.html --title "My course" npx -y scorm-mcp-server pack ./courses --batch --scorm-version 1.2 npx -y scorm-mcp-server validate pkg.zip # conformance-check an existing package npx -y scorm-mcp-server selftest # 1-second health check ``` **Web UI** — `ui` opens a localhost page: drop an .html or .zip, pick the SCORM edition and an optional pass mark, download the package. Runs entirely on your machine; nothing is uploaded anywhere. **Library** — `buildPackage()` is a public API for pipelines and SaaS backends: ```js import { buildPackage } from "scorm-mcp-server"; const r = await buildPackage({ html, title: "My course", scormVersion: "1.2", masteryScore: 0.6 }); // r.zip (Buffer) · r.fileName · r.warnings · r.milestoneIds … ``` **Diagnostic** — the `scorm_selftest` MCP tool packages a constant built-in HTML and reports version, duration and output path: it separates "server broken" from "input problem" in one second. ## Validate any SCORM package (v2.3) "Why does my LMS reject this zip?" — `scorm_validate` answers it for **any** SCORM package, not only those produced here, and the input is never modified: ```bash npx -y scorm-mcp-server validate course.zip # human-readable report npx -y scorm-mcp-server validate course.zip --json # machine-readable ``` Checks: zip readability, `imsmanifest.xml` at the ROOT (detects the classic *"zipped the folder instead of its contents"* mistake and says how to fix it), well-formed manifest, SCORM edition detection (2004/1.2), launchable organization/item/resource chain, launch file and every `` present in the archive (case-only mismatches flagged — they work on Windows and fail on Linux LMS servers), and full **XSD validation against the official ADL schemas** — using the package's own XSDs first and falling back to the embedded copies, so packages that ship without schemas validate too. Exit code 0/1 for CI pipelines; also exposed as the `scorm_validate` MCP tool and the `validatePackage()` library API. ## Install ### Option 0 — try it online, no install **[https://scormpackager.vercel.app](https://scormpackager.vercel.app)** — drop a course, pick the SCORM edition, download the package. Files are processed in memory and never stored, but they do travel to a server; for real work use the local options below, where nothing leaves your machine (and there is no 4 MB limit). ### Option A — one-click (recommended) Download **`scorm-mcp-server-x.y.z.mcpb`** from the [Releases](../../releases), then in **Claude Desktop → Settings → Extensions**, drag-drop the `.mcpb`, pick an output folder, and enable it. ### Option B — npm (any MCP client) No install step: add this to your client's MCP config (`~/Library/Application Support/Claude/claude_desktop_config.json` for Claude Desktop): ```json { "mcpServers": { "scorm": { "command": "npx", "args": ["-y", "scorm-mcp-server"], "env": { "SCORM_OUTPUT_DIR": "/ABSOLUTE/PATH/scorm-packages" } } } } ``` Registry name: **`io.github.giacomomaria81/scorm-mcp-server`** ([MCP registry](https://registry.modelcontextprotocol.io/v0/servers?search=scorm-mcp-server)). ### Option C — from source (developer) ```bash git clone && cd scorm-mcp-server npm install # dist/ is prebuilt; npm run build is optional ``` Then point the config at `node /ABSOLUTE/PATH/scorm-mcp-server/dist/index.js`. Restart Claude. The `scorm_package` tool is now available. ## Usage In a conversation: build your module with Claude Design, then say **"package this module as SCORM."** Claude calls `scorm_package` and returns the path to the `.zip`. ### Progress & completion — it just works **You don't have to prepare anything**: if your HTML declares no milestone, the packager **auto-generates them from the document structure** (sections → articles → headings, capped at 8, trigger `view`). Plain HTML gets meaningful progress out of the box. Disable with `auto_milestones: false`. Want `success_status = passed` on completion without touching the HTML? Pass `success_on_completion: true`. ### Declarative milestones (recommended for fine control) Mark the meaningful steps directly in your HTML — explicit milestones always take precedence over auto-generation. The runtime computes `progress_measure = milestones_reached / total`, and sets `completion_status = "completed"` once all are reached. | Attribute | Effect | |---|---| | `data-jalon="unique-id"` | declares a milestone | | `data-trigger="view"` | reached when scrolled into view (**default**) | | `data-trigger="click"` | reached on click | | `data-trigger="ended"` | reached when a video/audio ends | ```html
…
``` Recommended: **4–8 milestones per micro-module**. Resume is automatic (`cmi.suspend_data` + `cmi.location`); progress never regresses. **Programmatic milestones** — `window.SCORM2004.reach("quiz-passed")` works even if the id has no `data-jalon` element: unknown ids are **declared on the fly** and count in the total. To register one *before* it's reached (accurate denominator), use `window.SCORM2004.declare("quiz-passed")` early. Both survive resume. **Success status (opt-in)** — add `data-scorm-success="on-completion"` on any element (e.g. ``) and the runtime also sets `cmi.success_status="passed"` when the module completes. Without it, `success_status` is never written. **Language** — the tool's `language` (BCP-47, default `fr-FR`) is applied as `` when the source HTML doesn't declare one. **Security** — asset references are confined to the module folder: `../` or absolute paths outside it are never inlined (a warning is emitted instead). ## Test it without an LMS account Open `scorm-test-harness.html` via a tiny local server and drop a generated `.zip` into it: ```bash python3 -m http.server 8000 # then open http://localhost:8000/scorm-test-harness.html ``` You'll see live progress %, completion, and the full log of LMS API calls (0 errors expected). ## Build & test ```bash npm install npm run build # tsc -> dist/ npm test # 325 checks across 17 suites (xmllint required for the schema tests) # bonus: validate against a strict independent SCORM 2004 runtime npm i -D scorm-again && node test/scorm-again.test.mjs ``` Requirements: **Node ≥ 20**, and `xmllint` (`libxml2-utils`) for the schema test. ## Project structure ``` src/ index.ts (MCP server + CLI) · converter.ts (inlining + manifest + zip) · runtime.ts (injected SCORM runtime) · validate.ts (package conformance checker) · tom.ts (mobile-learning migration) · ui.ts (local web UI) dist/ compiled output (shipped) schemas/ 15 ADL XSD (SCORM 2004 4th Ed.) + schemas12/ (4 XSD SCORM 1.2), bundled into every package test/ 17 suites (converter / runtime / mcp / schema / validation / interactions / migration…) + fixtures ARCHITECTURE.md design decisions, data flow, testing strategy scorm-test-harness.html local browser SCORM player (fake LMS, no account) manifest.json MCPB manifest (for building the .mcpb desktop extension) ``` ## Privacy Policy This extension runs **entirely locally**: no data collection, no telemetry, no third parties. The only network activity is downloading assets that *your own HTML* references, to embed them into the offline package. Full policy: [PRIVACY.md](./PRIVACY.md). ## License [MIT](./LICENSE)