The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Limner listing page.

A Harnessed Image-generation Agent: Claude reasons, Cloudflare executes, D1 remembers, OAuth is the only way in.
Limner is an Agent Harness and Model Context Protocol (MCP) server for orchestrating image generation across multiple pipelines, with durable memory for project context. This repository is rasa, the foundation variant of the Limner family. You deploy rasa as your own instance: free as in beer, no strings or data pipelines attached.
Limner is explicitly built as a composable harness with clean seams:
/mcp. There is no private
side channel.The same tool surface ships three ways from one codebase: the OAuth-gated
Workers endpoint, a local stdio server, and a .mcpb one-click bundle for
Claude Desktop. The stdio server identifies itself as limner-mcp (preview):
stdio is the preview transport at v1, pending a refresh against the next MCP
spec revision. The Workers and .mcpb surfaces carry no preview tag.
The design did not arrive fully formed. The full record lives in docs/Limner_Cloudflare_CMA_Architecture.md: 22 dated decision records, a cost model, and five named off-ramp triggers that state in advance what would make us walk away from the approach.
Several of those records are reversals. The FastAPI shim that fronted the first design was killed. The Sharp/libvips composition path was abandoned for a hybrid WASM stack that runs inside the isolate. Recraft began as a composed first-party MCP and was later amended to a direct REST call, with the transport seam kept as a reusable adapter. One record is a correction: a request timed out at 30s during integration testing and was first blamed on an upstream API, until the API's own docs turned up an async mode and the fault turned out to be ours.
The "Deploy to Cloudflare" button is the cleanest dead-end (currently). We ran the spike on
2026-06-12. The button's monorepo mode extracts a single package into a standalone
repo, which severs the @limner/core workspace dependency and breaks the build.
Provisioning and secrets worked; the repo shape did not. The full note, and the
script that does the same job plus migrations and a smoke test, is under
Where is the Deploy to Cloudflare button?.
@limner/core (pipelines, composition, state),
@limner/mcp (the server, all three transports), @limner/cma-tools
(the same tool contract packaged for CMA custom-tool consumption)The architecture document with decision records lives at docs/Limner_Cloudflare_CMA_Architecture.md.
All tools are namespaced limner_*. Full schemas in
packages/limner-mcp/README.md.
| Tool | What it does | Needs |
|---|---|---|
limner_generate_dalle | OpenAI Images API (gpt-image-1 default) | OPENAI_API_KEY, your OpenAI credit |
limner_generate_recraft | Recraft, via their REST API (external.api.recraft.ai) | RECRAFT_API_KEY, your Recraft credit |
limner_generate_midjourney | Composes a Midjourney prompt string; a human carries it the rest of the way | Nothing |
limner_upscale / limner_vectorize | Recraft crisp upscale, and raster-to-SVG vectorize | RECRAFT_API_KEY, your Recraft credit |
limner_compose | 16 image ops behind one discriminated union: resize, crop, brightness, contrast, blur, sharpen, watermark, encode, decode, convert, renderText run in-isolate; cfTransform, cfOverlay, cfBlur, cfSmartCrop, cfBackgroundFill use Cloudflare Images | Images binding for the five cf* ops only |
limner_record / limner_recall / limner_forget / limner_list_categories | Durable memory with categories and idempotent upserts | D1 (or local SQLite) |
limner_create_project / limner_list_projects / limner_get_project_context / limner_record_project_note | Project briefs and running notes | D1 (or local SQLite) |
limner_health / limner_version / limner_list_pipelines / limner_pipeline_capabilities | Discovery and diagnostics | Nothing |
Most image tools start every session from zero. Limner records what a project
is and what you decided, then reads it back on the next run. Two stores back
this, both in D1 (or local SQLite for the stdio and .mcpb flavors):
limner_record writes a fact under a category with
an idempotent upsert, so re-recording the same key updates in place instead
of piling up duplicates. limner_recall reads a category back,
limner_forget removes an entry, and limner_list_categories enumerates
what exists. Use it for durable preferences: a house palette, a default
aspect ratio, a model that works for a given look.limner_create_project opens a project,
limner_record_project_note appends running notes (briefs, style decisions,
progress), limner_get_project_context returns the brief and notes for a
project, and limner_list_projects lists them. This is the "what were we
doing last week" layer.Nothing here is a vector database or an embedding model. It is plain structured state with explicit keys, which is the point: the agent recalls what it actually recorded, not the nearest fuzzy match.
Limner runs as a Claude Managed Agent: Anthropic's model does the reasoning (the brain), Cloudflare's Worker does the executing (the hands), and the two meet at the OAuth-gated MCP surface described above. The agent definition itself is narrow by design.
claude-sonnet-4-6, an A4-framed system
prompt (the framing is code-enforced: assertA4() refuses to build the
agent plan if the verbatim disclaimer is missing), and 16 Agent Skills
uploaded through the Skills API. That's it.Stand up your own agent:
limner-mcp Worker (Quickstart above) and note its
OAuth-protected MCP URL.ANTHROPIC_API_KEY in your environment.pnpm --filter @limner/core build && pnpm --filter @limner/limner-agent build.pnpm --filter @limner/limner-agent create-agent.
Review the printed request payloads, then re-run with -- --execute to
create the agent and upload all 16 skills.pnpm --filter @limner/limner-agent test-agent).Full reference, including the exact API endpoints, frontmatter validation rules, and the Path A / Path B tool-lockstep detail, is in packages/limner-agent/README.md.
One guidance core renders into three public surfaces: a skill for
autonomous multi-step agent work, a prompt for a launchable client
template, and a resource for a fact worth consulting. 19 guidance entries
in @limner/core render into 16 skills, 12 prompts, and 3 resources today. A
CI drift test enforces that a committed skill can never diverge from the
guidance entry that generated it.
16 skills (attach order; S = skill only, S+P = skill and prompt, S+R = skill and resource):
| Skill | Surface |
|---|---|
file-types | S+R |
external-tools | S+R |
midjourney | S+P |
dalle | S+P |
recraft | S+P |
illuminated-manuscript | S+P |
pipeline-router | S+P |
brand-stamp | S+P |
multi-size-export | S+P |
captioned-graphic | S+P |
iterate-on-asset | S |
style-from-images | S+P |
brand-kit | S |
art-research | S |
vectorize | S+P |
print-ready | S+R |
12 prompts: capability-tour, midjourney-builder, dalle-builder,
recraft-builder, illuminated-manuscript, pipeline-router, brand-stamp,
multi-size-export, captioned-graphic, aspect-ratio-crops,
style-from-images, vectorize.
3 resources: limner://reference/file-types,
limner://reference/external-tools, limner://reference/print-ready.
Together with the 18 tools in Tool Surface above, that's the whole public
capability surface. The full matrix (which guidance entry renders into which
surface, and why style-profile backs no standalone surface at all) is in
packages/limner-agent/README.md.
A few properties hold by construction, worth stating for anyone running their own instance:
__Host- cookie plus a signed hidden field);
the issued scope is pinned, and deny paths validate the redirect URI against
registration before redirecting.cf* ops reach
outside the isolate, and only with the provider key you supplied.Limner is bring-your-own-key software. It collects no analytics and sends no telemetry to the author. Anything that leaves your machine leaves only because a tool you called needs it:
limner_generate_dalle, Recraft for limner_generate_recraft and the
limner_upscale / limner_vectorize tools. Those providers process that data
under their own privacy terms. limner_generate_midjourney builds a prompt
string locally and calls nothing. In-isolate composition (limner_compose,
apart from the five cf* ops that use Cloudflare Images) needs no network and
no credentials.user_config) and used
only to authenticate those provider calls. They are never written to logs or
sent anywhere else.~/.limner/limner.db) on the stdio and .mcpb builds. This data stays until
you remove it with limner_forget or delete the database.Questions or requests about data handling go to jim@vinson.org.
For a self-deployed Workers stack:
cf* compose ops at real
usage volumesA note on the Images free tier: Cloudflare includes 5,000 unique transformations per month on every account, after which the cf* ops return errors until the month rolls over. That allowance is fine for kicking the tires; treat the paid plan as the requirement for actual use. The eleven in-isolate compose ops never touch Images and stay free everywhere.
Local stdio and .mcpb need no Cloudflare account at all: state goes to
local SQLite, and the cf* compose ops refuse cleanly.
Pick the heaviest option you have patience for. They all end at the same tool surface.
The script checks your wrangler login, provisions D1, KV, and R2 by name (re-running is safe), pins the resource ids into the wrangler config, applies the schema migration, builds, deploys, prompts for both provider keys, smoke-tests the deployed endpoint, and prints connect instructions for Claude Desktop, Claude Code, and MCP Inspector.
Optional: pnpm setup:cloudflare --with-example-seed loads a small generic
memory seed (a fictional postcard project) so the memory tools have
something to recall on day one. --env production provisions the separate
production environment, and --dry-run shows the plan without changing
anything.
Sigh... we tried, in a live spike (2026-06-12). The button's monorepo mode
extracts packages/limner-mcp into a standalone repository, which severs
the @limner/core workspace dependency, so the build cannot succeed
regardless of build settings. Its provisioning and secrets flow worked
well; the repo shape is the blocker. The script above does everything the
button would, plus migrations and a smoke test. If the button gains
in-place monorepo support, it returns here.
For push-to-deploy CI/CD in the meantime, connect your fork through the
dashboard: Workers and Pages, import the repository, root directory /,
build command pnpm install --frozen-lockfile && pnpm -r build, deploy
command pnpm --filter @limner/mcp run deploy. That flow keeps the full
repository, so the workspace resolves.
The script above is a convenience wrapper over five wrangler commands; the
by-hand version is in the comments of
packages/limner-mcp/wrangler.toml.
Short form: wrangler d1 create limner-rasa-dev, wrangler kv namespace create OAUTH_KV, wrangler r2 bucket create limner-rasa-artifacts-dev,
pin the printed ids in the config, then wrangler d1 migrations apply limner-rasa-dev --remote, wrangler secret put both keys, and
wrangler deploy.
For Claude Desktop, add to
~/Library/Application Support/Claude/claude_desktop_config.json:
.mcpb bundle (Claude Desktop one-click)Download the .mcpb from a GitHub release (built on mcpb-v* tags), open
it with Claude Desktop, and fill in the API-key prompts. Or build your own:
pnpm pack:mcpb.
Claude Desktop: Settings, Connectors, add a custom connector with the same
URL. MCP Inspector: npx @modelcontextprotocol/inspector, then connect
with the Streamable HTTP transport. OAuth dynamic client registration
handles credentials in all three cases.
docs/TESTING.md is the tester's checklist, ordered free-first and paid-last, with the cost of every step stated up front. Findings are public issues: use the test finding or bug report template.
Near-term, in rough order:
2026-07-28). Move the transports to the stateless
model the new revision defines and drop the per-session id. This is what
promotes stdio out of preview; the Workers and .mcpb surfaces are already
current.limner-pixel (pixel art) and limner-ascii (ASCII
workflows) build on rasa; see The Limner family.Limner is a family of model harnesses. Each variant builds on rasa (this repo) and adds opinions for a specific creative niche.
| Repo | Status | Focus |
|---|---|---|
vinsonconsulting/limner (this repo) | rasa, foundation | General-purpose; OSS |
vinsonconsulting/limner-pixel | in development, not yet public | Pixel art, sprite work, retro game asset pipelines |
vinsonconsulting/limner-ascii | pre-development skill building, not yet public | ASCII art workflows |
Specialty pipelines (pixel-art generators like Pixellab and RetroDiffusion)
are being built in limner-pixel (not yet public); rasa stays
general-purpose. The legacy proprietary work that preceded the OSS pivot is
kept private at vinsonconsulting/limner-pixel-legacy.
Contributions welcome. See CONTRIBUTING.md. All commits
need a Developer Certificate of Origin
sign-off via git commit -s; the DCO check gates merge.
Apache-2.0 © Jim Vinson · jimvinson.com
Limner is built on Anthropic's Claude Managed Agents platform and Cloudflare's CMA hosting. Limner is an independent project; "built on" does not imply endorsement by Anthropic or Cloudflare.
![]()
The hero above, translated to pixel art by PixelLab, the pipeline being built as limner-pixel. The family renders its parent.