Skip to content

Development ​

bash
pnpm --filter @spelling-creator/mcp start        # run the stdio server directly (or: pnpm dev:mcp)
pnpm --filter @spelling-creator/mcp test         # node --test over apps/mcp/test
pnpm --filter @spelling-creator/mcp build:views  # rebuild the interactive views after editing views/

The tests run on Node's built-in runner against a fake hub (test/fake-hub.js) and cover the tool surface end to end (smoke), validation, fact checking, images, forking and proposals, version history, live sessions, elicitation (the confirm prompts) and the views. CI runs them with every other package's tests (pnpm -r test in .github/workflows/ci.yml).

Where things live ​

FileResponsibility
src/stdio.jsLocal entry point (the package's bin): loads config, builds the server, connects stdio.
src/worker.jsRemote pieces the Worker composes: buildMcpServer, grantAuth, durableSessionStore. See Remote mode.
src/tools.jsTool definitions and handlers, transport-agnostic. Also exports SERVER_INFO.
src/collabTools.jsThe live-session tools. See Live sessions.
src/collab.jsThe document arithmetic behind them: decoding the room's state and working out Yjs updates.
src/standards.mdThe authoring standard's prose half: the rules that need judgment.
src/standards.jsThe seam that loads standards.md in both runtimes (with standards.node.js and standards.workerd.js).
src/validate.jsThe write path's side of validation, over the checks in @spelling-creator/core/lessonChecks. See Lesson validation.
src/api.jsThe hub client (the same Worker endpoints the web app uses).
src/git.jsVersion history: committing edits, forking, proposing, and reviewing or merging a proposal.
src/wikimedia.jsCommons search and download for search_images and add_image, over @spelling-creator/core/wikimedia.
src/images.jsContent hashing for uploaded image bytes.
src/auth.jsSupabase token rotation for stdio, and the shared refresh call.
src/config.jsEnvironment variables and the .env loader. See Configuration.
src/login.jsThe login helper.
src/views.jsThe ui:// resources behind interactive views.
views/Source for those views (markup and script), built into src/views/*.html.
scripts/pack.mjsBuilds the .mcpb bundle. See Packaging the bundle.

Most of the lesson logic the tools lean on lives in packages/core, so the web editor, the Worker and this server 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), @spelling-creator/core/lessonPatch applies id-addressed edit operations to an existing document, @spelling-creator/core/lessonChecks holds the checks, and the git/* modules handle the lesson repositories.

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 (globs **/*.md and **/*.html) to load it that way, and that rule 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). The built views reach both runtimes the same way, through #image-picker-html and #proposal-diff-html.

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 (scripts/build-views.mjs) 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.

Versions ​

apps/mcp/package.json and apps/mcp/manifest.json carry the same version, and both are bumped whenever the MCP server changes (not for web-only or core-only work). SERVER_INFO.version in src/tools.js, what the server reports to clients during the MCP handshake, is read from package.json (a JSON import), so there is nothing to bump there. test/version.test.js fails if manifest.json falls out of step.

Copyright © 2026 Spelling Creator.