Development
pnpm --filter @spelling-creator/mcp start # run the stdio server directly
pnpm --filter @spelling-creator/mcp test # doc-builder + auth + tool-surface + validation tests
pnpm --filter @spelling-creator/mcp build:views # rebuild the interactive views after editing views/Where things live
| File | Responsibility |
|---|---|
src/tools.js | Tool definitions and handlers, transport-agnostic. |
src/standards.md | The authoring standard's prose half: the rules that need judgement. |
src/validate.js | The write path's side of validation, over the checks in @spelling-creator/core/lessonChecks. See Lesson validation. |
src/api.js | The hub client (the same Worker endpoints the web app uses). |
src/git.js | Version history: committing edits, forking, proposing, and reviewing or merging a proposal. |
src/auth.js | Supabase token rotation. |
src/views.js | The ui:// resources behind interactive views. |
views/ | Source for those views (markup + script), built into src/views/*.html. |
Two pieces the tools lean on live in packages/core, so the web editor and the Worker can build and patch lessons the same way: @spelling-creator/core/lessonBuild builds the canonical editor document (and throws on input it can't turn into one), and @spelling-creator/core/lessonPatch applies id-addressed edit operations to an existing document.
src/standards.md is prose, so it is edited as a document rather than as an escaped JavaScript string, and src/standards.js is only the seam that loads it. Getting a markdown file into both runtimes takes a per-runtime resolution: #standards-md (a subpath import declared in apps/mcp/package.json) points at standards.workerd.js under the workerd condition, which imports the markdown as a Text module; wrangler needs the rules entry in apps/api/wrangler.jsonc to load .md that way, and it matches on the import specifier, which is why the shim names the file by a literal relative path. Everywhere else it points at standards.node.js, which reads the file off disk (src/ ships whole in the .mcpb bundle, so the path holds for an installed server too).
A view is one self-contained HTML file, because the host renders it in a sandboxed iframe that may fetch nothing at runtime. pnpm build:views bundles views/<name>.js with esbuild and inlines it into views/<name>.html, writing the result to src/views/. That output is committed: the server has no build step of its own (dev:mcp runs from source, the .mcpb bundle copies src/ wholesale, and wrangler reads the same file as a Text module), so shipping the built HTML keeps all three paths working. Rebuild and commit whenever you change anything under views/.
test/validate.test.js is built around one lesson written exactly to the standard, which must produce no errors and no warnings; the other cases mutate that fixture a rule at a time. Keep it that way: a false positive blocks an author who did nothing wrong, so the clean-lesson case is the one that matters most.