Development¶
How to hack on the NovelAI Image MCP server itself.
Quick start¶
# 1. Clone + sync the workspace virtualenv
git clone https://github.com/xinvxueyuan/NovelAI-Image-MCP.git
cd NovelAI-Image-MCP
uv sync
# 2. Install Node tooling (turbo, husky, markdownlint) — optional but recommended
corepack enable pnpm
pnpm install --frozen-lockfile
# 3. Run all checks
uv run --directory apps/server poe check
# or via turbo:
pnpm check # (runs: pnpm lint, pnpm typecheck, pnpm test via turbo)
Repository layout¶
NovelAI-Image-MCP/
├── apps/
│ ├── server/ # ← MCP server (Python package)
│ │ ├── src/novelai_image_mcp/
│ │ │ ├── server.py # MCPServer + lifespan
│ │ │ ├── settings.py # pydantic-settings env config
│ │ │ ├── cli.py / __main__.py # typer sync CLI
│ │ │ ├── output.py # save-image helper
│ │ │ ├── tools/ # 11 MCP tool definitions
│ │ │ │ ├── generate.py # generate_image / image_to_image / inpaint
│ │ │ │ ├── enhance.py # upscale / director / annotate
│ │ │ │ ├── tags.py # suggest_tags / encode_vibe
│ │ │ │ └── account.py # subscription / user_data / cost
│ │ │ └── nai/ # NovelAI HTTP client (httpx transport)
│ │ ├── tests/ # pytest + respx
│ │ ├── docker/smoke-test.py # container entrypoint smoke test
│ │ ├── Dockerfile # multi-stage production image
│ │ ├── pyproject.toml # server project + tool config (ruff/pyright/pytest/poe)
│ │ └── package.json # turbo-script wrappers (no Node deps)
│ └── docs/ # ← Sphinx documentation site
│ ├── source/ # MyST markdown sources
│ │ ├── conf.py # Sphinx configuration
│ │ ├── *.md # narrative docs
│ │ └── api/ # autodoc API reference
│ ├── Makefile # sphinx-build / live / linkcheck targets
│ ├── pyproject.toml # docs deps (sphinx, myst, furo)
│ └── package.json # turbo-script wrappers
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # ruff / pyright / pytest / docker smoke
│ │ ├── docs.yml # GitHub Pages deploy
│ │ └── release.yml # PyPI + GHCR + GitHub Release
│ └── actions/detect-changes/ # composite change-detection action
├── docker-compose.yml # orchestrates the server image
├── pyproject.toml # uv workspace root (virtual)
├── uv.lock # unified Python lockfile
├── pnpm-workspace.yaml # pnpm workspace declaration
├── package.json # root workspace + turbo + husky + markdownlint
├── pnpm-lock.yaml # unified Node lockfile
├── turbo.json # cross-workspace task definition
├── prek.toml # pre-commit hook config
├── .husky/pre-commit # husky pre-commit hook (4 phases)
├── REUSE.toml # SPDX license declarations
└── .markdownlint-cli2.jsonc # markdown lint config
Common commands¶
Action |
Command |
|---|---|
Sync all deps |
|
Run server (stdio) |
|
Run server (http) |
|
Run tests |
|
Lint Python |
|
Format Python |
|
Type-check |
|
All checks |
|
Build docs |
|
Live docs |
|
REUSE compliance |
|
The same commands work via turbo once pnpm install has run:
Action |
Turbo command |
|---|---|
Build all workspaces |
|
Lint all workspaces |
|
Type-check |
|
Test all workspaces |
|
Format |
|
Build docs only |
|
Live docs |
|
Clean all artifacts |
|
Contributing¶
See Contributing. TL;DR:
Open an issue describing your change.
Branch off
main.Implement + add tests.
uv run --directory apps/server poe checkmust pass.Open a PR with a clear description.
Testing¶
See Testing for the test layout, fixture strategy, and how to run a specific test.
Releasing¶
See Releasing for the cut-a-release process (PyPI + GHCR + GitHub Release + cosign signing).
Architecture¶
See Architecture for the server’s composition root, the
NovelAIClient design, and how tools wire into the MCP lifespan.