Several Dev.to posts describe community projects that connect Gemini’s stateful Interactions API for image generation and editing to different AI coding agents via the Model Context Protocol (MCP). The approach centers on the Gemini model gemini-3.1-flash-lite-image (nicknamed “NB2Lite”), which supports an Interactions API workflow where each image interaction returns an interaction_id that is reused to apply incremental edits across multiple turns while preserving the existing visual context. Each project wraps this capability in a small FastMCP server (nb2lite-agent) exposing four tools: generate_image, edit_image (stateful edits using the latest interaction_id), edit_local_image (edits an existing local image by uploading it), and get_help (reports configuration such as API key status, model, and output directory). The corresponding “skill” (for Antigravity CLI, Claude Code, Kiro, and Codex) provides workflow guidance, such as chaining only the newest interaction_id, using incremental edit descriptions, and selecting aspect ratio at generation time. The repos also provide installation paths, including plugin/skill marketplace installs, cloning and bootstrapping scripts, project-scoped installation, and optional Docker-based server runs. All posts include examples and describe “dogfooding,” noting that each article’s cover image is generated by the same skill and MCP tools described.
Community repos package Gemini stateful image editing as MCP skills for Antigravity, Codex, Claude Code, Kiro
Several Dev.to posts describe community projects that connect Gemini’s stateful Interactions API for image generation and editing to different AI coding agents via the Model Context Protocol (MCP). Th...
- The projects use Gemini’s Interactions API with an interaction_id to enable stateful image edits across multiple turns.
- A FastMCP server named nb2lite-agent exposes four tools: generate_image, edit_image, edit_local_image, and get_help.
- Stateful edits require chaining the latest interaction_id; edits from stale ids are described as causing session forking.
- Aspect ratio is chosen during generation and is inherited on subsequent stateful edits; changing it mid-session can degrade continuity.
- Each agent-specific repo packages the same MCP-based image workflow as a skill for Antigravity CLI, Claude Code, Kiro, or Codex, including installation and Docker options.
TL;DR: nb2lite-skill-agy wraps Google's gemini-3.1-flash-lite-image model (NB2Lite) in a FastMCP server and packages it as an Antigravity CLI skill. You type "generate an image of a cyberpunk kitchen" into Antigravity, 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. Background: why another image tool? 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 directly into Google Antigravity CLI, so your coding agent can generate and iteratively refine images as a natural part of a pair-programming session. It ships as two things in one repo: A Model Context Protocol (MCP) server (nb2lite-agent, a single-file FastMCP app in server.py) exposing four tools. A Skill definition (nb2lite-image) that teaches Antigravity when and how to use those tools well. The Interactions API: images with a memory The Interactions API is Gemini's stateful endpoint. The core loop looks like this: You call client.interactions.create(...) with a prompt and store=True. The response includes an interaction_id — a handle to the turn's visual context, persisted on Google's servers. On the next call, you pass 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 in Antigravity: "Add a lantern in its paw." That's it. The stored context holds the rest. A few practical details the server handles for you: Every turn returns a new interaction ID. Chain the latest one; editing from a stale ID silently forks your session from an older state. Aspect ratio is chosen at generation time (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. Thinking levels: 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. What is MCP, in one minute 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 like Antigravity CLI 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. Antigravity launches it, asks "what tools do you have?", and from then on the model can call them like native functions. The nb2lite-agent server exposes four core tools: 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 Antigravity can read and react to them gracefully. And what's an Agent skill? If MCP is the hands (the tools an agent can physically call), a skill is the muscle memory — a markdown file (SKILL.md) plus bundled resources that load into Antigravity'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: Call get_help first when diagnosing setup issues — if the API key is missing, nothing else will work. Keep edit prompts incremental: describe the change, not the scene. Always chain the latest interaction ID. Generations are billable — batch related edits and prefer 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 fully self-contained. Installing it into Antigravity CLI You need three things: Python 3.10+, Antigravity CLI, and a Gemini API key (free from Google AI Studio). Pick one of the paths below. Path A: The plugin marketplace (fewest keystrokes) Inside your Antigravity session, run: /plugin marketplace add xbill9/nb2lite-skill-agy /plugin install nb2lite-image@nb2lite-skill-agy This installs the skill and auto-registers the MCP server. The plugin manifest carries no API key — the server reads GEMINI_API_KEY from your environment, so make sure it's exported before launching Antigravity CLI. Path B: Clone and bootstrap (this repo) # 1. Get the code git clone https://github.com/xbill9/nb2lite-skill-agy.git cd nb2lite-skill-agy # 2. One-command setup: installs deps, registers the MCP server # in .mcp.json, and prompts for your API key (stored in ~/gemini.key) ./init.sh # 3. Restart Antigravity CLI in this directory and approve the server # when prompted. Verify with: /mcp # should list nb2lite-agent init.sh is idempotent and safe to rerun anytime. Path C: Install into your project From a clone of the repo: make init TARGET=/path/to/your/project ARGS='--output-dir ./images' This copies the skill into <project>/.gemini/antigravity-cli/skills/nb2lite-image/ and writes the nb2lite-agent entry into that project's .mcp.json. It reuses ~/gemini.key if available. Restart Antigravity in your project, approve the server, done. Path D: Docker (nothing on the host but Docker) The server is published as xbill9/nb2lite-agent: antigravity mcp add nb2lite-agent --env GEMINI_API_KEY="$(cat ~/gemini.key)" -- \ docker run --rm -i -e GEMINI_API_KEY -v "$PWD:$PWD" -w "$PWD" xbill9/nb2lite-agent The -v "$PWD:$PWD" -w "$PWD" mount ensures the container can save images to your workspace disk and read local files for edit_local_image. Troubleshooting /mcp doesn't list the server → restart Antigravity CLI in the project directory. Tools return 🔴 GEMINI_API_KEY is not set → run source set_env.sh (or export the key) and restart. Anything else → ask Antigravity to call get_help; it reports the live configuration. Examples: an Antigravity session in practice Once installed, you talk to Antigravity in plain English. A real flow looks like: You: "Generate a cozy cabin in a snowy forest at dusk, 16:9." Antigravity 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. Dogfooding: about that cover image 🐕🍖 "Eating your own dog food" means using your own product for real work. It's the difference between "this should work" and "I ship with this every day." This repo dogfoods itself at every layer: The skill is active inside its own repository — open Google Antigravity in a clone and the nb2lite-image skill and nb2lite-agent server are already wired up, so every development session doubles as an integration test. The integration tests (make test) drive the same four MCP tools an end user would, against the live API. And the cover image of this article was generated by the exact skill the article describes, from inside an Antigravity CLI session in this repo. One tool call, live generation, no retouching: generate_image( prompt="A wide tech blog cover illustration: a friendly AI agent with glowing antigravity elements floating alongside an easel, painting a vibrant galaxy, while a chain of connected frames behind it shows the same picture evolving step by step. Flat vector style, deep indigo background, neon cyan and magenta accents. Title text 'NB2Lite + Antigravity', subtitle 'Stateful image editing as an Antigravity skill'. Crisp, accurate lettering.", aspect_ratio="16:9", thinking_level="high", ) # 🟢 Image successfully saved! # • Saved to: /home/xbill/nb2lite-skill-agy/gen_1784832091_d71439ca.jpg # • Interaction ID: v1_ChdXbUJpYXB5aEZZYkotOFlQeC1UcG1BNBIXV21CaWFweWhGWWJKLThZUHgtVHBtQTQ (That exact output is committed to the repo as devto-cover.jpg, receipts and all.) Worth noticing: The text rendered correctly. "NB2Lite + Antigravity" came out crisp and typo-free — that's what thinking_level: "high" buys you on text-heavy layouts. The model illustrated its own pitch. The chain of frames (Initialize → Nebula Base → Enhance Detail → Refine → Stateful Edit) is the stateful edit loop — the image explains the Interactions API better than a manual diagram. If I wanted the accent color changed, I wouldn't regenerate — I'd edit_image with that interaction ID and say "make the cyan accents emerald." That's the whole point. Dogfooding is the cheapest credibility there is: the tool's real output is literally the first thing you saw when you opened this article. Links Repo: github.com/xbill9/nb2lite-skill-agy (Apache-2.0) Docker image: hub.docker.com/r/xbill9/nb2lite-agent Interactions API reference: ai.google.dev/api/interactions-api Model Context Protocol: modelcontextprotocol.io This is a third-party community project, not affiliated with or endorsed by Google. Bring your own Gemini API key — and remember generations are billable, so draft on low and save high for final outputs.
2 hours agoTL;DR: nb2lite-skill-codex wraps Google's gemini-3.1-flash-lite-image model (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. Background: why another image tool? 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: A Model Context Protocol (MCP) server (nb2lite-agent, a single-file FastMCP app in server.py) exposing exactly four tools. A Codex skill (nb2lite-image) that teaches Codex when and how to use those tools well. The Interactions API: images with a memory The Interactions API is Gemini's stateful endpoint. The core loop looks like this: You call client.interactions.create(...) with a prompt and store=True. The response includes an interaction_id — a handle to the turn's visual context, persisted on Google's servers. On the next call, you pass 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: Every turn returns a new interaction ID. Chain the latest one; editing from a stale ID silently forks your session from an older state (a subtle and very annoying bug if you roll this by hand). Aspect ratio is chosen at generation time (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. Thinking levels: 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. What is MCP, in one minute 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. And what's a Codex skill? 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: Call get_help first when diagnosing setup issues — if the API key is missing, nothing else will work. Keep edit prompts incremental: describe the change, not the scene. Always chain the latest interaction ID. Generations are billable — batch related edits and prefer 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. Installing the skill and MCP server in Codex You need Python 3.10+, Codex, and a Gemini API key from Google AI Studio. There are two pieces to install: The skill goes in .agents/skills/nb2lite-image/ for one project, or ~/.agents/skills/nb2lite-image/ for every project. The MCP registration tells Codex how to start 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. Path A: The plugin marketplace (fewest keystrokes) 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. Path B: Clone and bootstrap (this repo) # 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. Path C: Project-scoped install 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. Path D: User-wide skill and MCP registration 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. Path E: Manual Codex registration 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. Path F: Docker (nothing on the host but Docker) 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. Troubleshooting, the whole guide /mcp doesn't list the server → start or restart Codex in the directory that contains the project-scoped .codex/config.toml. The skill does not trigger → confirm SKILL.md exists at .agents/skills/nb2lite-image/SKILL.md (project) or ~/.agents/skills/nb2lite-image/SKILL.md (global), then restart Codex. Tools return 🔴 GEMINI_API_KEY is not set → run source set_env.sh (or export the key) and restart. Imports fail → run python3 -m pip install -r requirements.txt from the repo, or point it at the installed skill's mcp/requirements.txt. Anything else → ask Codex to call get_help; it reports the live config. Examples: a session in practice 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. Dogfooding: about that cover image 🐕🍖 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: The skill is active inside its own repository — open Codex in a clone and the nb2lite-image skill and nb2lite-agent server are already wired up, so every development session doubles as an integration test. The integration tests (make test) drive the same four MCP tools an end user would, against the live API. And now, the cover image of this article was made by the exact skill and MCP server the article describes, from inside a Codex session in this repo. The first call created a square neon-purple Codex mascot: 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: The square concept became a real editorial asset. edit_local_image is the bridge from any file on disk into the stateful workflow. The visual identity survived the format change. The graphite body, expressive eyes, and ultraviolet glow carry from the mascot study into the wide cover. The next change would be stateful. The local edit returned an interaction ID, so another request—"make the MCP nodes brighter"—would use 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. Links Repo: github.com/xbill9/nb2lite-skill-codex (Apache-2.0) Docker image: hub.docker.com/r/xbill9/nb2lite-agent Interactions API reference: ai.google.dev/api/interactions-api Model Context Protocol: modelcontextprotocol.io 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.
14 hours agoTL;DR: nb2lite-skill-kiro wraps Google's gemini-3.1-flash-lite-image model (NB2Lite) in a tiny FastMCP server and packages it as a Kiro skill. You type "generate an image of a cyberpunk kitchen" into Kiro, 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. Background: why another image tool? 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 Kiro, 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: A Model Context Protocol (MCP) server (nb2lite-agent, a single-file FastMCP app in server.py) exposing exactly four tools. A Kiro skill (nb2lite-image) that teaches Kiro when and how to use those tools well. The Interactions API: images with a memory The Interactions API is Gemini's stateful endpoint. The core loop looks like this: You call client.interactions.create(...) with a prompt and store=True. The response includes an interaction_id — a handle to the turn's visual context, persisted on Google's servers. On the next call, you pass 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: Every turn returns a new interaction ID. Chain the latest one; editing from a stale ID silently forks your session from an older state (a subtle and very annoying bug if you roll this by hand). Aspect ratio is chosen at generation time (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. Thinking levels: 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. What is MCP, in one minute 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 (Kiro, Claude Code, Claude 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. And what's a Kiro skill? If MCP is the hands (the tools Kiro can physically call), a skill is the muscle memory — a markdown file (SKILL.md) plus bundled resources that load into Kiro's context and teach it the workflow: which tool to reach for, in what order, with which constraints. Kiro skills live in .kiro/skills/<skill-name>/ inside a project. When Kiro detects a trigger phrase that matches the skill's description, it activates the skill automatically and starts using the guidance encoded there. For nb2lite-image, the skill encodes things like: Call get_help first when diagnosing setup issues — if the API key is missing, nothing else will work. Keep edit prompts incremental: describe the change, not the scene. Always chain the latest interaction ID. Generations are billable — batch related edits and prefer 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. Installing it: the "I just want it to work" edition You need three things: Python 3.10+, Kiro, and a Gemini API key (free from Google AI Studio). Pick one of the paths below. Path A: Clone and bootstrap (this repo) # 1. Get the code git clone https://github.com/xbill9/nb2lite-skill-kiro.git cd nb2lite-skill-kiro # 2. One-command setup: installs deps, registers the MCP server # in .mcp.json, and prompts for your API key (stored in ~/gemini.key) ./init.sh # 3. Restart Kiro in this directory and approve the server when prompted. That's genuinely it. init.sh is safe to rerun if anything looks off. Path B: Install into your project From a clone of the repo: make init TARGET=/path/to/your/project ARGS='--output-dir ./images' This copies the skill into <project>/.kiro/skills/nb2lite-image/ and writes the nb2lite-agent entry into that project's .mcp.json. It reuses ~/gemini.key if you've set one up. Restart Kiro in the target project, approve the server, done. Path C: Manual registration If you'd rather wire it up yourself: # Install deps pip install -r requirements.txt # Register the MCP server in your project's .mcp.json # (add this entry under "mcpServers") { "mcpServers": { "nb2lite-agent": { "command": "python", "args": [".kiro/skills/nb2lite-image/mcp/server.py"], "env": { "GEMINI_API_KEY": "your-key-here", "IMAGE_OUTPUT_DIR": "./images" } } } } Then copy the skill files into .kiro/skills/nb2lite-image/ and restart Kiro. Path D: Docker (nothing on the host but Docker) The server is published as xbill9/nb2lite-agent. Add this to your project's .mcp.json: { "mcpServers": { "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": { "GEMINI_API_KEY": "your-key-here" } } } } 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. Troubleshooting, the whole guide MCP server doesn't appear → restart Kiro in the project directory. Tools return 🔴 GEMINI_API_KEY is not set → run source set_env.sh (or export the key) and restart. Anything else → ask Kiro to call get_help; it reports the live config. Examples: a session in practice 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." Kiro 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. Dogfooding: about that cover image 🐕🍖 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: The skill is active inside its own repository — open Kiro in a clone and the nb2lite-image skill and nb2lite-agent server are already wired up, so every development session doubles as an integration test. The integration tests (make test) drive the same four MCP tools an end user would, against the live API. And now, the cover image of this article was generated by the exact skill the article describes, from inside a Kiro session in this repo. One tool call, first attempt, no retouching: generate_image( prompt="A sleek dark-themed tech cover image for a developer tool called " "'nb2lite-image'. The scene shows a futuristic AI workspace: a glowing " "terminal/IDE interface with code on the left side, and on the right, a " "vibrant image generation panel showing colorful AI-generated artwork " "materializing from pixels. In the center foreground, the Kiro logo — a " "stylized Kiro ghost with a neon blue glow — acts as the bridge between " "code and image, with light rays connecting it to both sides. Bold text " "reads 'nb2lite-image' at the top and 'Powered by Gemini • Built for Kiro' " "at the bottom. Color palette: deep navy background, electric blue and " "purple accents, white text, with sparkling particle effects. High-tech, " "modern, developer-aesthetic, cinematic lighting.", aspect_ratio="16:9", thinking_level="high", ) # 🟢 Image successfully saved! # • Saved to: cover-image.jpg # • Interaction ID: v1_ChdtbDloYW9iV0JaZlZqTWNQcHJDTXVBOBIXbWw5aGFvYldCWmZWak1jUHByQ011QTg (That exact output is committed to the repo as cover-image.jpg, receipts and all.) Worth noticing: The text rendered correctly. "nb2lite-image" at the top and the full tagline at the bottom came out crisp and typo-free — that's what thinking_level: "high" buys you on text-heavy layouts. Kiro is literally in the picture. The glowing "K" logo at center stage isn't decoration — it represents the actual agent orchestrating the generation. The skill generated an image that explains its own architecture. The dual-monitor composition tells the whole story. Code editor on the left, AI-generated artwork on the right, Kiro bridging the two — that's the exact workflow the article describes, visualized in one frame. If I wanted the accent color changed, I wouldn't regenerate — I'd edit_image with that interaction ID and say "shift the blue accents to purple." That's the whole point. Dogfooding is the cheapest credibility there is: no cherry-picked gallery, no "results may vary" fine print — the tool's real output is literally the first thing you saw when you opened this article. If the skill had flubbed the lettering or mangled the layout, you'd be looking at the evidence right now. Instead, the article ships with its own proof baked into the header. Links Repo: github.com/xbill9/nb2lite-skill-kiro (Apache-2.0) Docker image: hub.docker.com/r/xbill9/nb2lite-agent Interactions API reference: ai.google.dev/api/interactions-api Model Context Protocol: modelcontextprotocol.io This is a third-party community project, not affiliated with or endorsed by Anthropic or Google. Bring your own Gemini API key — and remember generations are billable, so draft on low and save high for the money shot.
1 day agoTL;DR: nb2lite-skill-claude wraps Google's gemini-3.1-flash-lite-image model in a tiny FastMCP server and packages it as a Claude Code skill. You type "generate an image of a cyberpunk kitchen" into Claude Code, 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. Background: why another image tool? 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 Nano Banana 2 Lite — 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 Claude Code, 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: A Model Context Protocol (MCP) server (nb2lite-agent, a single-file FastMCP app in server.py) exposing exactly four tools. A Claude Code skill (nb2lite-image) that teaches Claude when and how to use those tools well. The Interactions API: images with a memory The Interactions API is Gemini's stateful endpoint. The core loop looks like this: You call client.interactions.create(...) with a prompt and store=True. The response includes an interaction_id — a handle to the turn's visual context, persisted on Google's servers. On the next call, you pass 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: Every turn returns a new interaction ID. Chain the latest one; editing from a stale ID silently forks your session from an older state (a subtle and very annoying bug if you roll this by hand). Aspect ratio is chosen at generation time (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. Thinking levels: 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. What is MCP, in one minute 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 (Claude Code, Claude 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. And what's a Claude Code skill? If MCP is the hands (the tools Claude can physically call), a skill is the muscle memory — a markdown file (SKILL.md) plus bundled resources that load into Claude'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: Call get_help first when diagnosing setup issues — if the API key is missing, nothing else will work. Keep edit prompts incremental: describe the change, not the scene. Always chain the latest interaction ID. Generations are billable — batch related edits and prefer 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. Installing it: the "I just want it to work" edition You need three things: Python 3.10+, Claude Code, and a Gemini API key (free from Google AI Studio). Pick one of the paths below. Path A: The plugin marketplace (fewest keystrokes) Inside Claude Code, type: /plugin marketplace add xbill9/nb2lite-skill-claude /plugin install nb2lite-image@nb2lite-skill-claude This installs the skill and auto-registers the MCP server. The plugin manifest carries no API key (as it should!) — the server reads GEMINI_API_KEY from your environment, so make sure it's exported before launching Claude Code. Path B: Clone and bootstrap (this repo) # 1. Get the code git clone https://github.com/xbill9/nb2lite-skill-claude.git cd nb2lite-skill-claude # 2. One-command setup: installs deps, registers the MCP server # in .mcp.json, and prompts for your API key (stored in ~/gemini.key) ./init.sh # 3. Restart Claude Code in this directory and approve the server # when prompted. Verify with: /mcp # should list nb2lite-agent That's genuinely it. init.sh is safe to rerun if anything looks off. Path C: Install into your project From a clone of the repo: make init TARGET=/path/to/your/project ARGS='--output-dir ./images' This copies the skill into <project>/.claude/skills/nb2lite-image/ and writes the nb2lite-agent entry into that project's .mcp.json. It reuses ~/gemini.key if you've set one up. Restart Claude Code in the target project, approve the server, done. Path D: Docker (nothing on the host but Docker) The server is published as xbill9/nb2lite-agent: claude mcp add nb2lite-agent --env GEMINI_API_KEY="$(cat ~/gemini.key)" -- \ docker run --rm -i -e GEMINI_API_KEY -v "$PWD:$PWD" -w "$PWD" xbill9/nb2lite-agent 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. Troubleshooting, the whole guide /mcp doesn't list the server → restart Claude Code in the project directory. Tools return 🔴 GEMINI_API_KEY is not set → run source set_env.sh (or export the key) and restart. Anything else → ask Claude to call get_help; it reports the live config. Examples: a session in practice 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." Claude 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. Dogfooding: about that cover image 🐕🍖 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: The skill is active inside its own repository — open Claude Code in a clone and the nb2lite-image skill and nb2lite-agent server are already wired up, so every development session doubles as an integration test. The integration tests (make test) drive the same four MCP tools an end user would, against the live API. And now, the cover image of this article was generated by the exact skill the article describes, from inside a Claude Code session in this repo. One tool call, first attempt, no retouching: generate_image( prompt="A wide tech blog cover illustration: a friendly robot artist " "painting a glowing galaxy on an easel, while a chain of connected " "frames behind it shows the same picture evolving step by step " "(day sky, then sunset, then storm with lightning). Flat vector " "style, deep indigo background, neon cyan and orange accents. " "Title text 'NB2Lite + MCP', subtitle 'Stateful image editing " "as a Claude Code skill'. Crisp, accurate lettering.", aspect_ratio="16:9", thinking_level="high", ) # 🟢 Image successfully saved! # • Saved to: gen_1784759177_cbab8b65.jpg # • Interaction ID: v1_ChdpRU5hb2o3SWMzV2pNY1AtUFgy... (That exact output is committed to the repo as devto-cover.jpg, receipts and all.) Worth noticing: The text rendered correctly. "NB2Lite + MCP" and the full subtitle came out crisp and typo-free — that's what thinking_level: "high" buys you on text-heavy layouts. The model illustrated its own pitch. The chain of frames (day → sunset → storm → galaxy) is the stateful edit loop — the image explains the Interactions API better than a diagram I'd have drawn by hand. If I wanted the accent color changed, I wouldn't regenerate — I'd edit_image with that interaction ID and say "make the orange accents magenta." That's the whole point. Dogfooding is the cheapest credibility there is: no cherry-picked gallery, no "results may vary" fine print — the tool's real output is literally the first thing you saw when you opened this article. If the skill had flubbed the lettering or mangled the layout, you'd be looking at the evidence right now. Instead, the article ships with its own proof baked into the header. Links Repo: github.com/xbill9/nb2lite-skill-claude (Apache-2.0) Docker image: hub.docker.com/r/xbill9/nb2lite-agent Interactions API reference: ai.google.dev/api/interactions-api Model Context Protocol: modelcontextprotocol.io This is a third-party community project, not affiliated with or endorsed by Anthropic or Google. Bring your own Gemini API key — and remember generations are billable, so draft on low and save high for the money shot.
1 day ago
Amaal Mallik accuses Tanishk Bagchi of song credit theft and royalty issues
Music composer Amaal Mallik publicly accuses fellow composer Tanishk Bagchi of taking credit for songs and copying origi...
Silicon Valley startups urge Trump to keep access to Chinese open-weight AI models
Nearly 200 Silicon Valley companies and startup founders are urging the Trump administration not to restrict U.S. access...
Tech leaders, including Jensen Huang, cite AI skills and hiring despite past layoff claims
Recent reports portray a shift in how major tech executives discuss AI and jobs, with some leaders arguing AI adoption i...