Packaging the bundle
pnpm --filter @spelling-creator/mcp validate # check manifest.json against the MCPB schema
pnpm --filter @spelling-creator/mcp pack # build dist/spelling-creator-hub.mcpbpack stages a clean copy of the runtime files and vendors production dependencies with a flat npm install --omit=dev before zipping — necessary because this is a pnpm workspace whose node_modules are symlinks that wouldn't zip into a working bundle. The manifest (manifest.json) declares the Node server entry point, the user_config fields Claude Desktop collects (the refresh token is sensitive, so it's keychain-stored), and how they're injected as env vars the server already reads. Bundle artifacts (build/, dist/, *.mcpb) are gitignored.
The workspace dependency
@spelling-creator/core needs handling that npm can't provide. npm doesn't understand the workspace: protocol and fails the whole install on it, and a file: dependency would be symlinked — the exact thing that doesn't survive the zip. So pack copies the core modules the server actually imports into the staged node_modules as real files and removes the dependency from the staged manifest before npm sees it.
That works because those modules (richText, wikimedia) are dependency-free. Core's own dependency list — yjs, docx, isomorphic-git, supabase — belongs to its browser modules and has no business in this bundle. pack doesn't assume that silently: it walks the imports out from each module the server uses and fails the build if any of them reaches a real package, naming the package and the file:
Vendoring @spelling-creator/core assumes the modules this server imports are
dependency-free, but they now reach real packages:
dompurify (from src/richText.js)If that fires, either keep the module dependency-free or add the package to apps/mcp/package.json's dependencies so npm vendors it into the bundle.
Publishing a release (CI)
The .github/workflows/mcpb-release.yml workflow validates the manifest, runs the same pack command, and attaches the resulting .mcpb to a GitHub Release. It triggers two ways:
Push a tag matching
mcp-v*— the release is created for that tag:bash# bump the version in apps/mcp/manifest.json and package.json first, then: git tag mcp-v0.1.4 git push origin mcp-v0.1.4Run it manually from the Actions tab (
workflow_dispatch) — the tag and release name are derived from the version inapps/mcp/manifest.json(e.g.mcp-v0.1.4).
The asset is uploaded as spelling-creator-hub-<version>.mcpb. Users install it by opening that file with Claude Desktop.