
How nb2lite-skill-codex packages Google's gemini-3.1-flash-lite-image as a Codex skill + MCP server — with Codex-native install paths, stateful edits, and a dogfooded cover.
TL;DR: nb2lite-skill-codex wraps Google's
gemini-3.1-flash-lite-imagemodel (NB2Lite) in a tiny FastMCP server and packages it as a Codex skill. You type "generate an image of a cyberpunk kitchen" into Codex, and it just... does it. Then you say "add a neon RAMEN sign" and it edits the same image without re-prompting the whole scene. Oh, and the cover image of this article? Generated by the thing the article is about — dogfooding all the way down. More on that at the end.
Most image-generation workflows are stateless. You send a prompt, you get pixels back, and the model immediately forgets everything. Want to tweak the result? You re-describe the entire scene and pray the character, lighting, and composition survive the round trip. (Narrator: they don't.)
Google's NB2Lite — the friendly nickname for gemini-3.1-flash-lite-image — takes a different approach. It's a high-efficiency image model with sub-2-second generations, solid text rendering in 25+ languages, and — the headline feature — support for the stateful Interactions API, which lets you iterate on an image across multiple turns while the model keeps the visual context server-side.
This repo glues that capability into Codex, so your coding agent can generate and iteratively refine images as a natural part of a session. It ships as two things in one repo:
nb2lite-agent, a single-file FastMCP app in server.py) exposing exactly four tools.nb2lite-image) that teaches Codex when and how to use those tools well.The Interactions API is Gemini's stateful endpoint. The core loop looks like this:
client.interactions.create(...) with a prompt and store=True.interaction_id — a handle to the turn's visual context, persisted on Google's servers.previous_interaction_id, and the model edits the existing canvas — preserving character, style, lighting, and pixel continuity.So instead of this (stateless suffering):
"A watercolor fox in a forest at dawn, mist, soft light, wearing a red scarf, three birch trees on the left, and now also holding a lantern"
...you write this:
"Add a lantern in its paw."
That's it. The stored context holds the rest.
A few practical details the server handles for you:
1:1, 16:9, 9:16, 4:3, 3:4) and inherited on stateful edits — changing it mid-session degrades pixel continuity, so the edit tool deliberately doesn't accept one.low (default, fast drafts) or high (complex rendering, accurate text layout, character composition). The generic API spec also lists minimal and medium, but the live API rejects them for this model with an HTTP 400 — the server saves you from discovering that the hard way.The Model Context Protocol is an open standard for connecting AI assistants to tools and data. Before it, giving a model access to some service meant writing a bespoke integration for each assistant — N assistants × M services, everyone reinventing the same plumbing. MCP collapses that: a tool author writes one MCP server that exposes typed tools, and any MCP-capable client (Codex, Codex Desktop, and a growing list of others) can discover and call them with no per-client glue code.
An MCP server is usually a small local process that speaks JSON-RPC over stdio. The client launches it, asks "what tools do you have?", and from then on the model can call them like functions.
The nb2lite-agent server exposes exactly four:
| Tool | What it does |
|---|---|
generate_image | Text → 1k image. Saves locally, returns the path + an interaction ID. |
edit_image | Stateful edit: takes the previous interaction ID + a description of only the change. |
edit_local_image | Uploads any local image file inline (base64) and applies an edit — your entry point for existing files. |
get_help | Reports live config: API key status, active model, output directory, full tool reference. |
Images land on disk as gen_<timestamp>_<uuid8>.jpg (or edit_/edit_local_ prefixed) — the UUID suffix keeps concurrent generations from clobbering each other. Errors come back as 🔴 ... text strings rather than protocol errors, so the agent can read and react to them.
If MCP is the hands (the tools Codex can physically call), a skill is the muscle memory — a markdown file (SKILL.md) plus bundled resources that load into Codex's context and teach it the workflow: which tool to reach for, in what order, with which constraints.
For nb2lite-image, the skill encodes things like:
get_help first when diagnosing setup issues — if the API key is missing, nothing else will work.thinking_level: low for drafts.The skill also bundles the MCP server itself (mcp/server.py), its requirements, an installer script, and a vendored copy of the Interactions API developer guide — so it's self-contained: install the skill, and you have everything needed to also stand up the server.
You need Python 3.10+, Codex, and a Gemini API key from Google AI Studio. There are two pieces to install:
.agents/skills/nb2lite-image/ for one project, or ~/.agents/skills/nb2lite-image/ for every project.server.py and which environment-variable names to forward. The API key itself is never written into the Codex config.Pick one path below. Each path installs both pieces unless noted otherwise.
From a terminal, add the marketplace:
codex plugin marketplace add xbill9/nb2lite-skill-codex
Then open Codex's Plugins Directory and install NB2Lite Image (nb2lite-image).
The plugin manifest points Codex at both the packaged skill and .mcp.json, so
the nb2lite-agent server is registered automatically.
The plugin intentionally carries no secret. Before launching Codex, expose your key in the same shell:
export GEMINI_API_KEY="your-key"
codex
Approve the server when Codex prompts, then run /mcp; it should list
nb2lite-agent and its four tools.
# 1. Get the code
git clone https://github.com/xbill9/nb2lite-skill-codex.git
cd nb2lite-skill-codex
# 2. One-command setup: installs deps, refreshes the repository skill,
# and prompts for your API key (stored in ~/gemini.key)
./init.sh
# 3. Start or restart Codex from this checkout
codex
Inside Codex, approve the server and run /mcp. The checked-in
.codex/config.toml launches the authoritative root server.py; the repository
skill lives at .agents/skills/nb2lite-image/. init.sh is safe to rerun.
From a clone of the repo:
make init TARGET=/path/to/your/project ARGS='--output-dir ./images'
This copies the skill into <project>/.agents/skills/nb2lite-image/ and writes
an idempotent nb2lite-agent block into <project>/.codex/config.toml. The
server path is absolute, while IMAGE_OUTPUT_DIR can be project-relative.
cd /path/to/your/project
export GEMINI_API_KEY="your-key"
codex
Approve the server, then verify it with /mcp.
To make the skill available to every Codex project:
make init ARGS='--global'
This installs the skill under ~/.agents/skills/nb2lite-image/ and runs
codex mcp add to register nb2lite-agent in the user Codex configuration.
As with every other path, export GEMINI_API_KEY before starting Codex.
If you already copied the skill and only need the MCP server, Codex can register it directly:
python3 -m pip install -r /absolute/path/to/nb2lite-image/mcp/requirements.txt
codex mcp add nb2lite-agent \
--env GEMINI_MODEL_NAME=gemini-3.1-flash-lite-image \
-- python3 /absolute/path/to/nb2lite-image/mcp/server.py
For a project-scoped setup, the equivalent .codex/config.toml block is:
[mcp_servers.nb2lite-agent]
command = "python3"
args = ["/absolute/path/to/nb2lite-image/mcp/server.py"]
env_vars = ["GEMINI_API_KEY", "GOOGLE_API_KEY"]
[mcp_servers.nb2lite-agent.env]
GEMINI_MODEL_NAME = "gemini-3.1-flash-lite-image"
IMAGE_OUTPUT_DIR = "./images"
Notice the split: env_vars forwards secret values already present in the
shell; the [...env] table contains safe, non-secret defaults.
The server is published as xbill9/nb2lite-agent:
[mcp_servers.nb2lite-agent]
command = "docker"
args = ["run", "--rm", "-i", "-e", "GEMINI_API_KEY", "-v", "/abs/path/to/project:/abs/path/to/project", "-w", "/abs/path/to/project", "xbill9/nb2lite-agent"]
env_vars = ["GEMINI_API_KEY"]
The -v "$PWD:$PWD" -w "$PWD" mount matters: the server saves images to disk and reads local files for edit_local_image, so the container must see your project at the same absolute path as the host.
/mcp doesn't list the server → start or restart Codex in the directory that contains the project-scoped .codex/config.toml.SKILL.md exists at .agents/skills/nb2lite-image/SKILL.md (project) or ~/.agents/skills/nb2lite-image/SKILL.md (global), then restart Codex.🔴 GEMINI_API_KEY is not set → run source set_env.sh (or export the key) and restart.python3 -m pip install -r requirements.txt from the repo, or point it at the installed skill's mcp/requirements.txt.get_help; it reports the live config.Once installed, you talk to it in plain English. A real flow looks like:
You: "Generate a cozy cabin in a snowy forest at dusk, 16:9."
Codex calls:
generate_image(
prompt="A cozy log cabin in a snowy forest at dusk, warm light in the windows",
aspect_ratio="16:9",
thinking_level="low",
)
# 🟢 Saved to: ./gen_1784759001_a1b2c3d4.jpg
# Interaction ID: v1_ChdpRU5...
You: "Nice. Add smoke curling from the chimney."
edit_image(
previous_interaction_id="v1_ChdpRU5...",
edit_prompt="add gentle smoke curling from the chimney",
)
# 🟢 Saved to: ./edit_1784759050_e5f6a7b8.jpg
# Interaction ID: v1_Xk9mPq2... ← a NEW id; the next edit chains this one
You: "Now make it night, with aurora in the sky."
Same tool, newest ID, and the cabin, trees, and chimney smoke all stay put — only the sky changes. No re-prompting, no continuity roulette.
And for images that didn't come from the model at all:
You: "Take ./whiteboard-sketch.png and render it as a clean 3D product mockup."
edit_local_image(
image_path="./whiteboard-sketch.png",
edit_prompt="render this hand-drawn sketch as a high-fidelity 3D product mockup",
aspect_ratio="4:3",
)
It returns an interaction ID too — so follow-up refinements switch to edit_image and go stateful from there.
If the term is new to you: "eating your own dog food" means using your own product for real work, not just demoing it. It's the difference between "this should work" and "I ship with this every day." If a tool is good enough for your users, it should be good enough for you — and if it isn't, you'll be the first to feel the pain and fix it.
This repo dogfoods itself at every layer:
nb2lite-image skill and nb2lite-agent server are already wired up, so every development session doubles as an integration test.make test) drive the same four MCP tools an end user would, against the live API.generate_image(
prompt="A striking original Codex-inspired AI coding mascot, a friendly "
"compact futuristic robot glowing intense neon purple and "
"ultraviolet light, with a sleek graphite body, luminous violet "
"circuit patterns, floating code glyphs, and premium 3D character "
"illustration styling. No words, no watermark.",
aspect_ratio="1:1",
thinking_level="high",
)
# 🟢 Image successfully saved!
# • Saved to: gen_1784836047_64985fb9.jpg
# • Interaction ID: v1_Chd6bTlpYXZMWURhRzlfdU1Q...
The mascot worked, but a square image is the wrong shape for a dev.to header. Rather than taking the result into a separate graphics app, Codex fed the local output straight back through the MCP server:
edit_local_image(
image_path="./gen_1784836047_64985fb9.jpg",
edit_prompt="Adapt this exact neon-purple mascot into a wide 16:9 "
"technology article cover. Preserve its identity and style, "
"place it on the right third, extend a dark coding workspace "
"and subtle MCP node lines across the left, and leave strong "
"headline negative space. Include no words or watermark.",
aspect_ratio="16:9",
thinking_level="high",
)
# 🟢 Image successfully saved!
# • Saved to: edit_local_1784837547_e4c9df68.jpg
# • Interaction ID: v1_ChdxblZpYXVtTURxS2ZfdU1Q...
That second output is committed as devto-cover.jpg. No manual compositing or retouching: Codex discovered the skill, called the MCP tools, read the saved paths, and produced the artifact used by the article.
Worth noticing:
edit_local_image is the bridge from any file on disk into the stateful workflow.edit_image with the newest ID.Dogfooding is the cheapest credibility there is: the tool's real output is literally the first thing you saw when you opened this article. The cover also tests more than generation—it exercises Codex skill discovery, MCP tool registration, local-file editing, aspect-ratio adaptation, saving, and the handoff to a real publishing workflow.
This is a third-party community project, not affiliated with or endorsed by OpenAI or Google. Bring your own Gemini API key — and remember generations are billable, so draft on low and save high for the money shot.
aiHow nb2lite-skill-agy packages Google's gemini-3.1-flash-lite-image as an Antigravity skill + MCP server — with multi-turn stateful edits, a simple install guide, and a dogfooded cover image.
cloudGoogle just made multi-region Cloud Run significantly easier. Here is the full picture; what changed,...
aiHow omni-skill-agy packages Google's gemini-omni-flash-preview as an Antigravity CLI skill + MCP server — what Omni Flash actually does, a field guide to all eight tool calls, multi-turn stateful edits, an idiot-proof install guide, and a one-tool-call path to YouTube.
omnivideoTeaching Claude Code to Direct: A Stateful Video-Editing Skill Built on Gemini’s...
webdevThere is a super-new Rust Web Framework in town, and it's name is Topcoat Rust is already a solid...
aiPain Point: One Chinese Business Card is Actually Two Business Cards Business cards in...
Workflows from the Neura Market marketplace related to this Midjourney resource