novelai_image_mcp.nai — NovelAI HTTP client¶
The nai/ subpackage is the NovelAI HTTP client. It is MCP-agnostic —
it can be used standalone from any async Python code, independent of the
MCP server.
Submodules¶
novelai_image_mcp.nai.client¶
The high-level async client. Wraps the wire-format encoder
(payload.build_generation_payload) and response decoder
(response.parse_messagepack_images) over a shared
httpx.AsyncClient.
Complete NovelAI HTTP client built on a shared httpx async session.
A long-lived httpx.AsyncClient is owned by the instance (or supplied by the
MCP lifespan) and used for all NovelAI API traffic. Covers generation, Director,
utility, and account business logic.
- exception novelai_image_mcp.nai.client.MissingNovelAITokenError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIAuthenticationErrorNo usable NovelAI credential is configured.
- class novelai_image_mcp.nai.client.NovelAIClient(credentials, *, http_client=None, image_base_url='https://image.novelai.net', account_base_url='https://image.novelai.net', legacy_image_base_url='https://api.novelai.net', timeout=120.0, vibe_cache_entries=64)[source]¶
Bases:
objectProject-owned client for generation, tools, utilities, and account APIs.
- Parameters:
- async aclose()[source]¶
Close the underlying HTTP session when owned by this client.
- Return type:
None
- async generate(request)[source]¶
- Parameters:
request (GenerationRequest)
- Return type:
tuple[NovelAIImage, …]
- async stream_generation(request)[source]¶
Yield V4 / V4.5 / V5 events as the active driver produces chunks.
- Parameters:
request (GenerationRequest)
- Return type:
- async director(tool, image, *, prompt='', defry=0, emotion=None, emotion_level=EmotionLevel.NORMAL)[source]¶
- Parameters:
tool (DirectorTool)
image (bytes)
prompt (str)
defry (int)
emotion (Emotion | None)
emotion_level (EmotionLevel)
- Return type:
- async annotate(image, model)[source]¶
- Parameters:
image (bytes)
model (ControlNetModel)
- Return type:
- exception novelai_image_mcp.nai.client.NovelAIError(message, *, code=None, explanation=None)[source]¶
Bases:
ExceptionBase class for NovelAI failures.
codeis the NovelAI error code when known (Nonefor transport, timeout, or local validation failures that carry no server code);explanationis NovelAI’s official explanation for documented codes.
- exception novelai_image_mcp.nai.client.NovelAIProviderError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIErrorNovelAI rejected or failed the request.
- exception novelai_image_mcp.nai.client.NovelAIResponseError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIErrorNovelAI returned malformed or unsupported data.
- exception novelai_image_mcp.nai.client.NovelAITimeoutError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIErrorA request exceeded its timeout.
- exception novelai_image_mcp.nai.client.NovelAITransportError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIErrorThe request could not reach NovelAI.
- async novelai_image_mcp.nai.client.generate_image_from_plan(plan, *, client, n_samples=1, quality=True, uc_preset=0, noise_schedule='karras', cfg_rescale=0.0, dynamic_thresholding=False, auto_smea=False, prefer_brownian=True)[source]¶
Generate a single image from a high-level plan (used by the sync CLI).
The caller supplies the configured client plus the generation defaults (read from settings, not from a global config).
novelai_image_mcp.nai.constants¶
Enums for the NovelAI API surface: Action, Model, Sampler,
DirectorTool, Emotion, EmotionLevel, ControlNetModel, Endpoint,
NoiseSchedule. Plus the predicates is_v4_model, is_v5_model,
supports_vibe, and is_inpaint_model.
NovelAI protocol constants and model capability helpers.
- class novelai_image_mcp.nai.constants.IntEnum(new_class_name, /, names, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
-
Enum where members are also (and must be) ints
- class novelai_image_mcp.nai.constants.StrEnum(new_class_name, /, names, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]¶
-
Enum where members are also (and must be) strings
- __new_member__(*values)¶
values must already be of type str
- class novelai_image_mcp.nai.constants.Endpoint(*values)[source]¶
Bases:
StrEnumNovelAI API path constants.
Most paths are rooted at
https://image.novelai.net(the consolidated third-party host). Two exceptions —UPSCALEandANNOTATE— were not migrated toimage.novelai.netand remain on the Primary API athttps://api.novelai.net. The Primary API’s docs (https://api.novelai.net/docs/) state that third-party users may use its/ai/routes. SeeNovelAIClient.legacy_image_base_url.The
/ai/generate-imagefamily is split by model generation:IMAGE(/ai/generate-image) — used by V3 / Furry legacy models. Returns a ZIP archive (application/zip) with HTTP 201.IMAGE_STREAM(/ai/generate-image-stream) — used by V4 / V4.5 / V5 new models. Returns a MessagePack stream (stream: "msgpack") with HTTP 200. The selection is driven byis_v4_modelinclient.py.
- LOGIN = '/user/login'¶
- USER_DATA = '/user/data'¶
- SUBSCRIPTION = '/user/subscription'¶
- IMAGE = '/ai/generate-image'¶
- IMAGE_STREAM = '/ai/generate-image-stream'¶
- DIRECTOR = '/ai/augment-image'¶
- ENCODE_VIBE = '/ai/encode-vibe'¶
- UPSCALE = '/ai/upscale'¶
- ANNOTATE = '/ai/annotate-image'¶
- SUGGEST_TAGS = '/ai/generate-image/suggest-tags'¶
- class novelai_image_mcp.nai.constants.Model(*values)[source]¶
Bases:
StrEnum- V3 = 'nai-diffusion-3'¶
- V3_INPAINT = 'nai-diffusion-3-inpainting'¶
- FURRY = 'nai-diffusion-furry-3'¶
- FURRY_INPAINT = 'nai-diffusion-furry-3-inpainting'¶
- V4 = 'nai-diffusion-4-full'¶
- V4_INPAINT = 'nai-diffusion-4-full-inpainting'¶
- V4_CURATED = 'nai-diffusion-4-curated-preview'¶
- V4_CURATED_INPAINT = 'nai-diffusion-4-curated-inpainting'¶
- V4_5 = 'nai-diffusion-4-5-full'¶
- V4_5_INPAINT = 'nai-diffusion-4-5-full-inpainting'¶
- V4_5_CURATED = 'nai-diffusion-4-5-curated'¶
- V4_5_CURATED_INPAINT = 'nai-diffusion-4-5-curated-inpainting'¶
- V5 = 'nai-diffusion-5-full'¶
- V5_CURATED = 'nai-diffusion-5-curated'¶
- V5_INPAINT = 'nai-diffusion-5-full-inpainting'¶
- class novelai_image_mcp.nai.constants.Action(*values)[source]¶
Bases:
StrEnum- GENERATE = 'generate'¶
- IMG2IMG = 'img2img'¶
- INPAINT = 'infill'¶
- class novelai_image_mcp.nai.constants.Sampler(*values)[source]¶
Bases:
StrEnum- EULER = 'k_euler'¶
- EULER_ANCESTRAL = 'k_euler_ancestral'¶
- DPM_2S_ANCESTRAL = 'k_dpmpp_2s_ancestral'¶
- DPM_2M = 'k_dpmpp_2m'¶
- DPM_2M_SDE = 'k_dpmpp_2m_sde'¶
- DPM_SDE = 'k_dpmpp_sde'¶
- DDIM = 'ddim_v3'¶
- class novelai_image_mcp.nai.constants.NoiseSchedule(*values)[source]¶
Bases:
StrEnum- NATIVE = 'native'¶
- KARRAS = 'karras'¶
- EXPONENTIAL = 'exponential'¶
- POLYEXPONENTIAL = 'polyexponential'¶
- class novelai_image_mcp.nai.constants.ControlNetModel(*values)[source]¶
Bases:
StrEnum- PALETTE_SWAP = 'hed'¶
- FORM_LOCK = 'midas'¶
- SCRIBBLER = 'fake_scribble'¶
- BUILDING_CONTROL = 'mlsd'¶
- LANDSCAPER = 'uniformer'¶
- class novelai_image_mcp.nai.constants.DirectorTool(*values)[source]¶
Bases:
StrEnum- LINE_ART = 'lineart'¶
- SKETCH = 'sketch'¶
- BACKGROUND_REMOVAL = 'bg-removal'¶
- DECLUTTER = 'declutter'¶
- COLORIZE = 'colorize'¶
- EMOTION = 'emotion'¶
- class novelai_image_mcp.nai.constants.Emotion(*values)[source]¶
Bases:
StrEnum- NEUTRAL = 'neutral'¶
- HAPPY = 'happy'¶
- SAD = 'sad'¶
- ANGRY = 'angry'¶
- SCARED = 'scared'¶
- SURPRISED = 'surprised'¶
- TIRED = 'tired'¶
- EXCITED = 'excited'¶
- NERVOUS = 'nervous'¶
- THINKING = 'thinking'¶
- CONFUSED = 'confused'¶
- SHY = 'shy'¶
- DISGUSTED = 'disgusted'¶
- SMUG = 'smug'¶
- BORED = 'bored'¶
- LAUGHING = 'laughing'¶
- IRRITATED = 'irritated'¶
- AROUSED = 'aroused'¶
- EMBARRASSED = 'embarrassed'¶
- WORRIED = 'worried'¶
- LOVE = 'love'¶
- DETERMINED = 'determined'¶
- HURT = 'hurt'¶
- PLAYFUL = 'playful'¶
- class novelai_image_mcp.nai.constants.EmotionLevel(*values)[source]¶
Bases:
IntEnum- NORMAL = 0¶
- SLIGHTLY_WEAK = 1¶
- WEAK = 2¶
- EVEN_WEAKER = 3¶
- VERY_WEAK = 4¶
- WEAKEST = 5¶
- novelai_image_mcp.nai.constants.is_v4_model(model)[source]¶
True for structured-prompt (V4+) models: V4 / V4.5 / V5 families.
These use the
/ai/generate-image-streamendpoint (MessagePack) and thev4_prompt/v4_negative_promptcaption payload shape. The name is retained for backward compatibility (publicnaiAPI).
- novelai_image_mcp.nai.constants.is_v5_model(model)[source]¶
True for NovelAI Diffusion V5 models (Full / Curated / Inpainting).
- novelai_image_mcp.nai.constants.supports_vibe(model)[source]¶
True when vibe transfer (reference images) works for this model.
Vibe transfer is available on V4 / V4.5 only — V3 / Furry and V5 (feature not yet released for V5 per the official announcement) return False.
- novelai_image_mcp.nai.constants.params_version_for(model)[source]¶
Return the NovelAI
params_versionfor a model.V5 clients ship both
3(Marinara-Engine) and4(Auto-NovelAI, SDlab) and the API tolerates either; this project sends4for V5 (the value used by the most recent third-party implementations) and3for older generations, pending live verification.
novelai_image_mcp.nai.models¶
Pydantic models for the wire format: GenerationRequest,
CharacterPrompt, NovelAIGenerationPlan.
Pure value objects shared by the NovelAI generation pipeline.
- novelai_image_mcp.nai.models.dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)[source]¶
Add dunder methods based on the fields defined in the class.
Examines PEP 526 __annotations__ to determine fields.
If init is true, an __init__() method is added to the class. If repr is true, a __repr__() method is added. If order is true, rich comparison dunder methods are added. If unsafe_hash is true, a __hash__() method is added. If frozen is true, fields may not be assigned to after instance creation. If match_args is true, the __match_args__ tuple is added. If kw_only is true, then by default all fields are keyword-only. If slots is true, a new class with a __slots__ attribute is returned.
- class novelai_image_mcp.nai.models.Action(*values)[source]¶
Bases:
StrEnum- GENERATE = 'generate'¶
- IMG2IMG = 'img2img'¶
- INPAINT = 'infill'¶
- class novelai_image_mcp.nai.models.Model(*values)[source]¶
Bases:
StrEnum- V3 = 'nai-diffusion-3'¶
- V3_INPAINT = 'nai-diffusion-3-inpainting'¶
- FURRY = 'nai-diffusion-furry-3'¶
- FURRY_INPAINT = 'nai-diffusion-furry-3-inpainting'¶
- V4 = 'nai-diffusion-4-full'¶
- V4_INPAINT = 'nai-diffusion-4-full-inpainting'¶
- V4_CURATED = 'nai-diffusion-4-curated-preview'¶
- V4_CURATED_INPAINT = 'nai-diffusion-4-curated-inpainting'¶
- V4_5 = 'nai-diffusion-4-5-full'¶
- V4_5_INPAINT = 'nai-diffusion-4-5-full-inpainting'¶
- V4_5_CURATED = 'nai-diffusion-4-5-curated'¶
- V4_5_CURATED_INPAINT = 'nai-diffusion-4-5-curated-inpainting'¶
- V5 = 'nai-diffusion-5-full'¶
- V5_CURATED = 'nai-diffusion-5-curated'¶
- V5_INPAINT = 'nai-diffusion-5-full-inpainting'¶
- class novelai_image_mcp.nai.models.NoiseSchedule(*values)[source]¶
Bases:
StrEnum- NATIVE = 'native'¶
- KARRAS = 'karras'¶
- EXPONENTIAL = 'exponential'¶
- POLYEXPONENTIAL = 'polyexponential'¶
- class novelai_image_mcp.nai.models.Sampler(*values)[source]¶
Bases:
StrEnum- EULER = 'k_euler'¶
- EULER_ANCESTRAL = 'k_euler_ancestral'¶
- DPM_2S_ANCESTRAL = 'k_dpmpp_2s_ancestral'¶
- DPM_2M = 'k_dpmpp_2m'¶
- DPM_2M_SDE = 'k_dpmpp_2m_sde'¶
- DPM_SDE = 'k_dpmpp_sde'¶
- DDIM = 'ddim_v3'¶
- novelai_image_mcp.nai.models.is_v4_model(model)[source]¶
True for structured-prompt (V4+) models: V4 / V4.5 / V5 families.
These use the
/ai/generate-image-streamendpoint (MessagePack) and thev4_prompt/v4_negative_promptcaption payload shape. The name is retained for backward compatibility (publicnaiAPI).
- novelai_image_mcp.nai.models.normalize_tags(values)[source]¶
Trim tags and remove case-insensitive duplicates while preserving order.
- novelai_image_mcp.nai.models.normalize_negative_prompt(value)[source]¶
Split comma/newline negative prompt text into normalized tags.
- class novelai_image_mcp.nai.models.CharacterIntent(description: 'str', tags: 'tuple[str, ...]', negative_tags: 'tuple[str, ...]', center: 'PositionCoord')[source]¶
Bases:
object- Parameters:
- center: PositionCoord¶
- class novelai_image_mcp.nai.models.GenerationHints(width: 'int | None' = None, height: 'int | None' = None, steps: 'int | None' = None, scale: 'float | None' = None, sampler: 'str | None' = None, seed: 'int | None' = None, negative_tags: 'tuple[str, ...]' = ())[source]¶
Bases:
object- Parameters:
- class novelai_image_mcp.nai.models.PromptIntent(source_language: 'str', english_description: 'str', base_tags: 'tuple[str, ...]', generation: 'GenerationHints', characters: 'tuple[CharacterIntent, ...]', search_required: 'bool', search_query: 'str | None', search_reason: 'str | None')[source]¶
Bases:
object- Parameters:
source_language (str)
english_description (str)
generation (GenerationHints)
characters (tuple[CharacterIntent, ...])
search_required (bool)
search_query (str | None)
search_reason (str | None)
- generation: GenerationHints¶
- characters: tuple[CharacterIntent, ...]¶
- class novelai_image_mcp.nai.models.VisualResearch(facts: 'tuple[str, ...]', sources: 'tuple[str, ...]')[source]¶
Bases:
object
- class novelai_image_mcp.nai.models.TipoRequest(description: 'str', tags: 'tuple[str, ...]', visual_facts: 'tuple[str, ...]', seed: 'int')[source]¶
Bases:
object
- class novelai_image_mcp.nai.models.TipoPrompt(natural_language: 'str', tags: 'tuple[str, ...]')[source]¶
Bases:
object
- class novelai_image_mcp.nai.models.GenerationOverrides(width: 'int | None' = None, height: 'int | None' = None, steps: 'int | None' = None, scale: 'float | None' = None, sampler: 'str | None' = None, seed: 'int | None' = None, negative_prompt: 'str | None' = None)[source]¶
Bases:
object- Parameters:
- class novelai_image_mcp.nai.models.NovelAIGenerationPlan(prompt: 'str', negative_prompt: 'str', width: 'int', height: 'int', steps: 'int', scale: 'float', sampler: 'str', seed: 'int', base_caption: 'str', char_captions: 'tuple[dict[str, object], ...]', character_prompts: 'tuple[dict[str, object], ...]', use_coords: 'bool')[source]¶
Bases:
object- Parameters:
- class novelai_image_mcp.nai.models.CharacterPrompt(prompt: 'str', negative_prompt: 'str' = '', x: 'float' = 0.5, y: 'float' = 0.5, enabled: 'bool' = True)[source]¶
Bases:
object
- class novelai_image_mcp.nai.models.GenerationRequest(prompt, base_caption=None, model=Model.V4_5, action=Action.GENERATE, negative_prompt='', width=832, height=1216, n_samples=1, steps=28, scale=5.0, sampler=Sampler.EULER_ANCESTRAL, seed=0, extra_noise_seed=None, noise_schedule=NoiseSchedule.KARRAS, quality=True, uc_preset=0, dynamic_thresholding=False, cfg_rescale=0.0, smea=None, smea_dynamic=None, auto_smea=False, image=None, mask=None, strength=None, noise=None, add_original_image=True, controlnet_strength=1.0, controlnet_condition=None, controlnet_model=None, references=(), reference_information=(), reference_strengths=(), character_prompts=(), use_coords=False, use_order=True, legacy_uc=False, normalize_reference_strengths=True, deliberate_euler_ancestral_bug=False, prefer_brownian=True, skip_cfg_above_sigma=None, legacy=False, legacy_v3_extend=False, inpaint_img2img_strength=None, straight_alpha=False)[source]¶
Bases:
objectComplete NovelAI generation request independent of the chat planner.
- Parameters:
prompt (str)
base_caption (str | None)
model (Model)
action (Action)
negative_prompt (str)
width (int)
height (int)
n_samples (int)
steps (int)
scale (float)
seed (int)
extra_noise_seed (int | None)
noise_schedule (NoiseSchedule | str)
quality (bool)
uc_preset (int)
dynamic_thresholding (bool)
cfg_rescale (float)
smea (bool | None)
smea_dynamic (bool | None)
auto_smea (bool)
image (str | None)
mask (str | None)
strength (float | None)
noise (float | None)
add_original_image (bool)
controlnet_strength (float)
controlnet_condition (str | None)
controlnet_model (str | None)
character_prompts (tuple[CharacterPrompt, ...])
use_coords (bool)
use_order (bool)
legacy_uc (bool)
normalize_reference_strengths (bool)
deliberate_euler_ancestral_bug (bool)
prefer_brownian (bool)
skip_cfg_above_sigma (int | None)
legacy (bool)
legacy_v3_extend (bool)
inpaint_img2img_strength (int | None)
straight_alpha (bool)
- noise_schedule: NoiseSchedule | str¶
- character_prompts: tuple[CharacterPrompt, ...]¶
- estimate_anlas_cost(*, opus=False)[source]¶
Estimate provider Anlas cost using the public web-client formula.
Note: on V5, Opus free generations (normal resolution, <=28 steps) are subject to NovelAI’s refillable “battery” usage limit; once exhausted the API silently bills Anlas instead of erroring, so the estimate here covers the paid cost regardless of quota state.
novelai_image_mcp.nai.auth¶
Argon2id access-key derivation + per-request tracking headers.
NovelAI credential validation and access-key derivation.
- novelai_image_mcp.nai.auth.dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)[source]¶
Add dunder methods based on the fields defined in the class.
Examines PEP 526 __annotations__ to determine fields.
If init is true, an __init__() method is added to the class. If repr is true, a __repr__() method is added. If order is true, rich comparison dunder methods are added. If unsafe_hash is true, a __hash__() method is added. If frozen is true, fields may not be assigned to after instance creation. If match_args is true, the __match_args__ tuple is added. If kw_only is true, then by default all fields are keyword-only. If slots is true, a new class with a __slots__ attribute is returned.
- class novelai_image_mcp.nai.auth.datetime(year, month, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]])¶
Bases:
dateThe year, month and day arguments are required. tzinfo may be None, or an instance of a tzinfo subclass. The remaining arguments may be ints.
- astimezone()¶
tz -> convert to local time in new timezone tz
- classmethod combine()¶
date, time -> datetime with same date and time fields
- ctime()¶
Return ctime() style string.
- date()¶
Return date object with same year, month and day.
- dst()¶
Return self.tzinfo.dst(self).
- fold¶
- classmethod fromisoformat(object, /)¶
string -> datetime from a string in most ISO 8601 formats
- classmethod fromtimestamp()¶
timestamp[, tz] -> tz’s local time from POSIX timestamp.
- hour¶
- isoformat()¶
[sep] -> string in ISO 8601 format, YYYY-MM-DDT[HH[:MM[:SS[.mmm[uuu]]]]][+HH:MM]. sep is used to separate the year from the time, and defaults to ‘T’. The optional argument timespec specifies the number of additional terms of the time to include. Valid options are ‘auto’, ‘hours’, ‘minutes’, ‘seconds’, ‘milliseconds’ and ‘microseconds’.
- max = datetime.datetime(9999, 12, 31, 23, 59, 59, 999999)¶
- microsecond¶
- min = datetime.datetime(1, 1, 1, 0, 0)¶
- minute¶
- classmethod now(tz=None)¶
Returns new datetime object representing current time local to tz.
- tz
Timezone object.
If no tz is specified, uses local timezone.
- replace()¶
Return datetime with new specified fields.
- resolution = datetime.timedelta(microseconds=1)¶
- second¶
- classmethod strptime()¶
string, format -> new datetime parsed from a string (like time.strptime()).
- time()¶
Return time object with same time but with tzinfo=None.
- timestamp()¶
Return POSIX timestamp as float.
- timetuple()¶
Return time tuple, compatible with time.localtime().
- timetz()¶
Return time object with same time and tzinfo.
- tzinfo¶
- tzname()¶
Return self.tzinfo.tzname(self).
- classmethod utcfromtimestamp()¶
Construct a naive UTC datetime from a POSIX timestamp.
- classmethod utcnow()¶
Return a new datetime representing UTC day and time.
- utcoffset()¶
Return self.tzinfo.utcoffset(self).
- utctimetuple()¶
Return UTC time tuple, compatible with time.localtime().
- class novelai_image_mcp.nai.auth.blake2b(data=b'', *, digest_size=64, key=b'', salt=b'', person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, node_depth=0, inner_size=0, last_node=False, usedforsecurity=True, string=None)¶
Bases:
objectReturn a new BLAKE2b hash object.
- MAX_DIGEST_SIZE = 64¶
- MAX_KEY_SIZE = 64¶
- PERSON_SIZE = 16¶
- SALT_SIZE = 16¶
- block_size¶
- copy()¶
Return a copy of the hash object.
- digest()¶
Return the digest value as a bytes object.
- digest_size¶
- hexdigest()¶
Return the digest value as a string of hexadecimal digits.
- name¶
- update(data, /)¶
Update this hash object’s state with the provided bytes-like object.
- class novelai_image_mcp.nai.auth.NovelAICredentials(token: 'str | None' = None, username: 'str | None' = None, password: 'str | None' = None)[source]¶
Bases:
object
- novelai_image_mcp.nai.auth.derive_access_key(credentials)[source]¶
Derive the 64-character NovelAI login key from username/password.
- Parameters:
credentials (NovelAICredentials)
- Return type:
novelai_image_mcp.nai.payload¶
build_generation_payload — wire-format encoder (MessagePack-compatible).
NovelAI V4+ / V5 wire payload mapping.
- class novelai_image_mcp.nai.payload.Model(*values)[source]¶
Bases:
StrEnum- V3 = 'nai-diffusion-3'¶
- V3_INPAINT = 'nai-diffusion-3-inpainting'¶
- FURRY = 'nai-diffusion-furry-3'¶
- FURRY_INPAINT = 'nai-diffusion-furry-3-inpainting'¶
- V4 = 'nai-diffusion-4-full'¶
- V4_INPAINT = 'nai-diffusion-4-full-inpainting'¶
- V4_CURATED = 'nai-diffusion-4-curated-preview'¶
- V4_CURATED_INPAINT = 'nai-diffusion-4-curated-inpainting'¶
- V4_5 = 'nai-diffusion-4-5-full'¶
- V4_5_INPAINT = 'nai-diffusion-4-5-full-inpainting'¶
- V4_5_CURATED = 'nai-diffusion-4-5-curated'¶
- V4_5_CURATED_INPAINT = 'nai-diffusion-4-5-curated-inpainting'¶
- V5 = 'nai-diffusion-5-full'¶
- V5_CURATED = 'nai-diffusion-5-curated'¶
- V5_INPAINT = 'nai-diffusion-5-full-inpainting'¶
- novelai_image_mcp.nai.payload.is_v4_model(model)[source]¶
True for structured-prompt (V4+) models: V4 / V4.5 / V5 families.
These use the
/ai/generate-image-streamendpoint (MessagePack) and thev4_prompt/v4_negative_promptcaption payload shape. The name is retained for backward compatibility (publicnaiAPI).
- novelai_image_mcp.nai.payload.is_v5_model(model)[source]¶
True for NovelAI Diffusion V5 models (Full / Curated / Inpainting).
- novelai_image_mcp.nai.payload.params_version_for(model)[source]¶
Return the NovelAI
params_versionfor a model.V5 clients ship both
3(Marinara-Engine) and4(Auto-NovelAI, SDlab) and the API tolerates either; this project sends4for V5 (the value used by the most recent third-party implementations) and3for older generations, pending live verification.
- class novelai_image_mcp.nai.payload.GenerationRequest(prompt, base_caption=None, model=Model.V4_5, action=Action.GENERATE, negative_prompt='', width=832, height=1216, n_samples=1, steps=28, scale=5.0, sampler=Sampler.EULER_ANCESTRAL, seed=0, extra_noise_seed=None, noise_schedule=NoiseSchedule.KARRAS, quality=True, uc_preset=0, dynamic_thresholding=False, cfg_rescale=0.0, smea=None, smea_dynamic=None, auto_smea=False, image=None, mask=None, strength=None, noise=None, add_original_image=True, controlnet_strength=1.0, controlnet_condition=None, controlnet_model=None, references=(), reference_information=(), reference_strengths=(), character_prompts=(), use_coords=False, use_order=True, legacy_uc=False, normalize_reference_strengths=True, deliberate_euler_ancestral_bug=False, prefer_brownian=True, skip_cfg_above_sigma=None, legacy=False, legacy_v3_extend=False, inpaint_img2img_strength=None, straight_alpha=False)[source]¶
Bases:
objectComplete NovelAI generation request independent of the chat planner.
- Parameters:
prompt (str)
base_caption (str | None)
model (Model)
action (Action)
negative_prompt (str)
width (int)
height (int)
n_samples (int)
steps (int)
scale (float)
seed (int)
extra_noise_seed (int | None)
noise_schedule (NoiseSchedule | str)
quality (bool)
uc_preset (int)
dynamic_thresholding (bool)
cfg_rescale (float)
smea (bool | None)
smea_dynamic (bool | None)
auto_smea (bool)
image (str | None)
mask (str | None)
strength (float | None)
noise (float | None)
add_original_image (bool)
controlnet_strength (float)
controlnet_condition (str | None)
controlnet_model (str | None)
character_prompts (tuple[CharacterPrompt, ...])
use_coords (bool)
use_order (bool)
legacy_uc (bool)
normalize_reference_strengths (bool)
deliberate_euler_ancestral_bug (bool)
prefer_brownian (bool)
skip_cfg_above_sigma (int | None)
legacy (bool)
legacy_v3_extend (bool)
inpaint_img2img_strength (int | None)
straight_alpha (bool)
- noise_schedule: NoiseSchedule | str¶
- character_prompts: tuple[CharacterPrompt, ...]¶
- estimate_anlas_cost(*, opus=False)[source]¶
Estimate provider Anlas cost using the public web-client formula.
Note: on V5, Opus free generations (normal resolution, <=28 steps) are subject to NovelAI’s refillable “battery” usage limit; once exhausted the API silently bills Anlas instead of erroring, so the estimate here covers the paid cost regardless of quota state.
- class novelai_image_mcp.nai.payload.NovelAIGenerationPlan(prompt: 'str', negative_prompt: 'str', width: 'int', height: 'int', steps: 'int', scale: 'float', sampler: 'str', seed: 'int', base_caption: 'str', char_captions: 'tuple[dict[str, object], ...]', character_prompts: 'tuple[dict[str, object], ...]', use_coords: 'bool')[source]¶
Bases:
object- Parameters:
- novelai_image_mcp.nai.payload.build_payload(plan, *, model)[source]¶
Map an already-resolved plan to NovelAI’s V4.5/V5 request shape.
- Parameters:
plan (NovelAIGenerationPlan)
model (str)
- Return type:
novelai_image_mcp.nai.response¶
Response parsing: NovelAIImage, parse_messagepack_images,
parse_zip_images, check_status, GenerationEvent.
NovelAI HTTP, ZIP, and MessagePack response parsing.
- novelai_image_mcp.nai.response.dataclass(cls=None, /, *, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False)[source]¶
Add dunder methods based on the fields defined in the class.
Examines PEP 526 __annotations__ to determine fields.
If init is true, an __init__() method is added to the class. If repr is true, a __repr__() method is added. If order is true, rich comparison dunder methods are added. If unsafe_hash is true, a __hash__() method is added. If frozen is true, fields may not be assigned to after instance creation. If match_args is true, the __match_args__ tuple is added. If kw_only is true, then by default all fields are keyword-only. If slots is true, a new class with a __slots__ attribute is returned.
- class novelai_image_mcp.nai.response.Any(*args, **kwargs)[source]¶
Bases:
objectSpecial type indicating an unconstrained type.
Any is assignable to every type.
Any assumed to have all methods and attributes.
All values are assignable to Any.
Note that all the above statements are true from the point of view of static type checkers. At runtime, Any cannot be used with instance checks.
- novelai_image_mcp.nai.response.cast(typ, val)[source]¶
Cast a value to a type.
This returns the value unchanged. To the type checker this signals that the return value has the designated type, but at runtime we intentionally don’t check anything (we want this to be as fast as possible).
- novelai_image_mcp.nai.response.explain(code)[source]¶
Return NovelAI’s official explanation for a documented error code.
Undocumented codes (including API-supplied MessagePack stream codes) return
None— the API’s own message is then the best explanation.
- novelai_image_mcp.nai.response.parse_error_body(content)[source]¶
Extract
(code, message)from a NovelAI error body.NovelAI returns JSON in several shapes —
{"statusCode", "message"},{"code", "message"},{"error": ...}, or a bare{"message"}— or, on some legacy paths, raw text. Falls back to the decoded text when neither a code nor a message field is present.
- class novelai_image_mcp.nai.response.NovelAIImage(filename: 'str', data: 'bytes')[source]¶
Bases:
object
- class novelai_image_mcp.nai.response.GenerationEvent(event_type: 'str', sample_index: 'int', step_index: 'int', generation_id: 'str', sigma: 'float', image: 'NovelAIImage')[source]¶
Bases:
object- Parameters:
- image: NovelAIImage¶
- novelai_image_mcp.nai.response.check_status(status_code, content)[source]¶
Raise the domain error associated with an HTTP status.
The NovelAI error code (body
statusCode/code, or the HTTP status when the body carries none) and its official explanation are attached to the raised exception and appended to its message.
- novelai_image_mcp.nai.response.parse_zip_images(content)[source]¶
- Parameters:
content (bytes)
- Return type:
tuple[NovelAIImage, …]
- class novelai_image_mcp.nai.response.MessagePackStreamParser[source]¶
Bases:
objectIncrementally parse NovelAI’s big-endian length-prefixed MessagePack.
- feed(chunk)[source]¶
- Parameters:
chunk (bytes)
- Return type:
tuple[GenerationEvent, …]
- novelai_image_mcp.nai.response.parse_messagepack_events(content)[source]¶
- Parameters:
content (bytes)
- Return type:
tuple[GenerationEvent, …]
- novelai_image_mcp.nai.response.parse_messagepack_images(content)[source]¶
- Parameters:
content (bytes)
- Return type:
tuple[NovelAIImage, …]
Note
The exception classes listed above are imported into response for
raising in check_status; they are documented under their defining
module, novelai_image_mcp.nai.exceptions (see the next subsection).
novelai_image_mcp.nai.exceptions¶
Domain exception hierarchy:
Typed NovelAI domain failures.
Every exception optionally carries the NovelAI error code (HTTP status or
API-supplied stream code) and, when documented, NovelAI’s official
explanation — see nai.errors. The code and explanation are appended to
the message (so they reach MCP tool callers and the CLI) and exposed as
attributes for programmatic handling.
- exception novelai_image_mcp.nai.exceptions.NovelAIError(message, *, code=None, explanation=None)[source]¶
Bases:
ExceptionBase class for NovelAI failures.
codeis the NovelAI error code when known (Nonefor transport, timeout, or local validation failures that carry no server code);explanationis NovelAI’s official explanation for documented codes.
- exception novelai_image_mcp.nai.exceptions.NovelAIProviderError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIErrorNovelAI rejected or failed the request.
- exception novelai_image_mcp.nai.exceptions.NovelAIValidationError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIProviderErrorThe request is invalid.
- exception novelai_image_mcp.nai.exceptions.NovelAIAuthenticationError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIProviderErrorCredentials are missing, invalid, or expired.
- exception novelai_image_mcp.nai.exceptions.NovelAIInsufficientCreditsError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIProviderErrorThe account has insufficient Anlas or no subscription.
- exception novelai_image_mcp.nai.exceptions.NovelAIConcurrencyError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIProviderErrorThe account hit a concurrency or rate limit.
- exception novelai_image_mcp.nai.exceptions.NovelAITimeoutError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIErrorA request exceeded its timeout.
- exception novelai_image_mcp.nai.exceptions.NovelAITransportError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIErrorThe request could not reach NovelAI.
- exception novelai_image_mcp.nai.exceptions.NovelAIResponseError(message, *, code=None, explanation=None)[source]¶
Bases:
NovelAIErrorNovelAI returned malformed or unsupported data.
novelai_image_mcp.nai.errors¶
Error-body parsing and official error-code explanations: ErrorInfo,
OFFICIAL_CODE_EXPLANATIONS, explain, parse_error_body.
NovelAI error-body parsing and official error-code explanations.
Every error raised toward NovelAI’s API carries the error code and —
where documented — NovelAI’s official explanation text (see
OFFICIAL_CODE_EXPLANATIONS). Codes sent inside MessagePack stream errors by
the API itself ({code, message} events) are passed through verbatim.
- novelai_image_mcp.nai.errors.OFFICIAL_CODE_EXPLANATIONS: dict[int, str] = {400: 'invalid request (validation failed)', 401: 'authentication failed (missing, invalid, or expired credentials)', 402: 'active subscription or Anlas required', 409: 'request conflicted with server state', 429: 'rate limit or concurrency limit reached'}¶
Official error codes documented by NovelAI’s API docs (https://api.novelai.net/docs) and the official FAQ. 5xx are deliberately folded into the generic provider-error bucket rather than enumerated.
- class novelai_image_mcp.nai.errors.ErrorInfo(code, message, raw)[source]¶
Bases:
objectNormalized error payload extracted from a NovelAI error body.
- novelai_image_mcp.nai.errors.explain(code)[source]¶
Return NovelAI’s official explanation for a documented error code.
Undocumented codes (including API-supplied MessagePack stream codes) return
None— the API’s own message is then the best explanation.
- novelai_image_mcp.nai.errors.parse_error_body(content)[source]¶
Extract
(code, message)from a NovelAI error body.NovelAI returns JSON in several shapes —
{"statusCode", "message"},{"code", "message"},{"error": ...}, or a bare{"message"}— or, on some legacy paths, raw text. Falls back to the decoded text when neither a code nor a message field is present.
novelai_image_mcp.nai.service¶
create_novelai_client factory + NovelAIConfigLike protocol. The factory
takes a settings-like object and an httpx.AsyncClient, returns a fully
wired NovelAIClient.
High-level NovelAI service construction for the MCP server and CLI consumers.
A duck-typed config object (NovelAIConfigLike Protocol) keeps the nai
package decoupled from the MCP wrapper. The wrapper’s NovelAISettings
(pydantic-settings, env-driven) satisfies this protocol structurally; see
novelai_image_mcp.settings.
- class novelai_image_mcp.nai.service.NovelAIConfigLike(*args, **kwargs)[source]¶
Bases:
ProtocolStructural shape of any object supplying NovelAI client configuration.
- novelai_image_mcp.nai.service.create_novelai_client(config, *, http_client=None)[source]¶
Create a complete client from a configuration object.
Accepts either a token or a complete username/password pair. Pass a shared
http_clientwhen the caller (e.g. the MCP lifespan) wants connection pooling across requests; otherwise the client owns a private session.- Parameters:
config (NovelAIConfigLike)
http_client (httpx.AsyncClient | None)
- Return type:
Official error codes¶
Every NovelAI error raised by this client carries the error code (the
HTTP status, or the API’s own stream code) and, where documented, NovelAI’s
official explanation. Both are appended to the message and exposed as
attributes on the exception:
Code |
Meaning (official) |
|---|---|
|
Invalid request (validation failed) |
|
Authentication failed |
|
Active subscription or Anlas required |
|
Request conflict |
|
Rate limit / concurrency limit |
|
NovelAI upstream error |
All exceptions derive from NovelAIError and expose .code /
.explanation (both None for transport, timeout, and local validation
failures that carry no server code). See the novelai_image_mcp.nai.errors
module for the parsing helpers.