Lolly
Buksan ang App

MCP Server

signed by Lollymcp.htmlHTMLpixels, not shapes12 KBAI generatedgenerated by ClaudeI-check mo mismoKunin ang signed na file

Connect an AI assistant or an agent runtime to Lolly and it can make finished, on-brand files for you: a QR code, a badge, a chart or a slide deck, as SVG, PNG, PDF and more. Every answer comes with an editable link, so you can open the result in Lolly and adjust it by hand.

The quickest start needs no account. In Claude Code, run:

claude mcp add --transport http lolly https://lolly.tools/api/mcp

Then ask: "Use Lolly to make a QR code for https://example.com as an SVG."

Behind that request the agent lists the tools, reads one tool's inputs, renders it and hands you the file. Tools are data, so a new tool reaches a server as a catalog update, with no change to the server's code.

Each server draws with the design system of its own content profile. The public server on lolly.tools uses Lolly Demo. Read the lolly://design-context resource to see which design system a server uses, with its colours, type, logos and house rules.

AI agents compares this server with the other ways an agent can drive Lolly. This page is the reference for the server itself.

Choose an endpoint

Every endpoint offers the same twenty-two tools. They differ in who can connect and which formats they can make, because only some run a headless browser.

EndpointWho can connectWhat it makes
https://lolly.tools/api/mcp, the open endpointAnyone. No token and no sign-in; calls are limited per address and per day (see Limits on the open endpoint).Vector (svg, emf, eps, eps-cmyk, dxf, penpot), data and text (html, md, txt, json, csv, ics, vcf) and, for SVG-native tools, png plus the float formats exr and hdr (with hdr set).
https://mcp.lolly.tools/mcp, the full endpointWhoever holds the access token from the operator, by OAuth or bearer.Everything a tool declares: vector, every raster format, print PDF (CMYK and crop marks included) and animation and video (gif, apng, webm, mp4).
Your own machine, over stdio or local HTTPYou. No token is needed over stdio.Every format, once the scoped browser is installed with pnpm run mcp:install-browser. In a checkout, the server builds the web shell it renders through on first use.

Start with the open endpoint: it needs no setup and covers every SVG-native tool. Tools laid out in HTML, such as design and chart, draw in the browser tier, so rendering or looking at them needs the full endpoint or your own machine. So does print PDF. Large files and heavy automation also belong on your own machine.

Limits on the open endpoint

lolly.tools/api/mcp is free to use and has a known daily cost, so these limits apply to it:

  • 60 calls a minute per address. A 429 carries Retry-After.
  • A daily budget for the whole endpoint, shared by every caller: a fixed amount of compute and data transfer per UTC day. When the budget is spent, every call returns 503 with error: daily_budget_reached and a Retry-After that counts down to 00:00 UTC.
  • PNG up to 1600 x 1600 pixels of area (enough for 1920 x 1080). A larger request is scaled down to fit, and the render result says so. Ask for svg for a file that scales to any size.
  • Answers up to 4.4 MB. A larger answer comes back as a tool error that says why. The operator sets this with LOLLY_MCP_RESPONSE_MAX_BYTES. Anonymous calls never receive a server-held download link.
  • Decks up to 100 slides per call, for lolly_rebrand, lolly_read, lolly_check and lolly_compose. A PDF counts its pages, and a source deck or inventory given to lolly_check or lolly_compose is held to the same limit.
  • Up to 262,144 characters of slide text measured per lolly_compose call, counting each extra measure that fit: "shrink" makes. A text slot past that comes back unmeasured with a compose.fit.unmeasured note, and the result says how many slots were not measured.
  • Files up to about 3.2 MiB each for the tools that take a file, so that a file and its base64 encoding fit in one request of up to 4.5 MB (LOLLY_MCP_REQUEST_MAX_BYTES). A refusal gives the endpoint's exact figure in bytes.

Lolly on your own machine lifts these limits, except that a file can still be at most 24 MiB.

A render on any endpoint runs the same render path a person's export runs. The server honours the full parameter contract (width, height, unit, dpi, colour profile, PDF password and, for the motion formats, fps, seconds, wait, codec and vq), and it never watermarks or embeds anything a person's own download would not. The same path and settings do not mean the same bytes for every format; see how far byte-identical output goes.

Connect a client

The open endpoint (no token)

Point any MCP client that speaks Streamable HTTP at https://lolly.tools/api/mcp. There is nothing to sign in to:

{
  "mcpServers": {
    "lolly": { "type": "http", "url": "https://lolly.tools/api/mcp" }
  }
}

In Claude Code the same connection is one command:

claude mcp add --transport http lolly https://lolly.tools/api/mcp

In a hosted assistant, add a custom connector with that URL and leave the OAuth fields blank. A quick check with curl (expect a JSON list of the twenty-two tools):

curl -s -X POST https://lolly.tools/api/mcp \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

The full endpoint (access token)

mcp.lolly.tools authenticates against a shared access token, which your Lolly operator holds. The token is never printed in a link or a log.

As a custom connector (OAuth). The full endpoint is a stateless OAuth 2.1 authorization server, so it works with any MCP client that supports custom connectors:

  • In your client's connector settings, add a custom connector pointing at https://mcp.lolly.tools/mcp. Hosted assistants usually put this under a Connectors or Integrations panel.
  • Leave the OAuth Client ID and Secret blank. The server registers your client itself (dynamic client registration).
  • The client finds the OAuth server and opens a consent page. Paste the access token and approve.

Then ask the agent to "list the Lolly tools" or "render the wordmark tool as a PNG."

With a bearer token (CLI or any HTTP client). The full endpoint also accepts the raw token, so scripted clients skip the OAuth steps. Most MCP clients take a config entry like:

{
  "mcpServers": {
    "lolly": {
      "type": "http",
      "url": "https://mcp.lolly.tools/mcp",
      "headers": { "Authorization": "Bearer <your-access-token>" }
    }
  }
}

A quick check with curl (expect a JSON list of the twenty-two tools, plus the files_* tools when the connection is scoped; no token returns 401):

curl -s -X POST https://mcp.lolly.tools/mcp \
  -H "authorization: Bearer <your-access-token>" \
  -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Install Lolly in ChatGPT

ChatGPT connects to Lolly through a plugin, which lets it join a document someone invites it into. Pasting an invitation into a chat cannot install tools, so install the plugin first.

Open Plugins in ChatGPT, choose Create custom MCP server, name the plugin Lolly and set the server URL to https://lolly.tools/api/mcp/agents. Choose No authentication, review the connection notice and create the plugin. Install the resulting plugin, then enable Lolly in your conversation. Account and workspace policies decide whether custom MCP connections are available. See OpenAI's connection guide.

Then copy a fresh invitation from Share in the open document and give it to ChatGPT. Agents in an open document covers the invitation, what the agent can do and how to pause or disconnect the agent.

The installed plugin has no account-wide credential: each invitation selects its instance, document and permissions. It works with public HTTPS Lolly relays on any domain, including lolly.ing. Calls to private networks and local addresses need the local MCP server instead. Directory publication is separate from installing a custom MCP server: Lolly's stable plugin identity is on lolly.tools, and a document invitation selects the instance where the work happens.

How the hosted connector handles a call

The hosted connector keeps no active document or invitation between calls, so the agent passes the same invitation on every call. The relay keeps the connection while the editor stays open. Editing requires documentId, ifRevision and transactionId; retry with the same arguments. An ended invitation grants no access.

The connector uses the public gateway's durable request limits and usage budget, and refuses requests when admission is unavailable. Outbound calls pin a checked public DNS address, verify TLS for the original hostname, refuse redirects and carry only the document bearer capability. Operators can switch the connector off with LOLLY_DISABLE_AGENT_CONNECTOR=1.

Plugin use

Lolly's software uses the Mozilla Public License 2.0, including its permissions, conditions, warranty disclaimer and limits of liability. Documentation uses CC BY-SA 4.0. Assets have their own licenses. Using the plugin does not change your rights in your content or grant rights in somebody else's assets. Check agent edits before sharing them. Your agent provider and a private instance's operator may have their own terms.

Installing the plugin grants no account-wide access. Keep invitations private. Document data passes through the connector and invitation relay to your agent provider; see the privacy policy. Report setup problems through Lolly's issue tracker, and leave private invitations and document content out of public reports.

On your own machine

From a checkout, pnpm run mcp starts the server over stdio, with no token. The local server lists the twenty-two tools, the six files_* tools and the nine live tools that join an open document (Agents in an open document). pnpm run mcp:http runs the long-lived HTTP service instead. Run the server yourself covers hosting one for other people.

How an agent uses the server

The server's own prompts walk an agent through the same order:

  • Find a tool. lolly_list_tools, usually with a focused q and a small limit.
  • Read its inputs. lolly_describe_tool returns the exact input schema, formats, templates and examples.
  • Check, when needed. lolly_validate before you hand out a URL you built yourself. lolly_render checks inputs on its own.
  • Render. lolly_render returns the file and the editable link.
  • Prove it, when asked. lolly_verify shows that a file the agent holds is an untouched Lolly export.

Every input and export control is a URL parameter, defined by URL mode and Reserved URL parameters. The MCP query argument, a share link, the CLI's --flag=value pairs and the hot-link render URL are one contract under four transports, so an agent that has learnt the table has learnt all four. The reserved parameters an agent reaches for most are format, width, height, unit and dpi, profile, password, bleed and marks, the provenance switches (c2pa, imprint, durable, meta), _madewith (the design-system record, on unless _madewith=0), hdr and depth, cuts, s, lang, emoji and emojifx, plus fps, seconds, wait, codec and vq for the motion formats.

Formats are per tool: you can only ask for one the tool declares, and lolly_describe_tool lists them. The call is the same whether you ask a QR tool for svg or an animated tool for mp4; the server decides how to produce the file. Every format Lolly can open and make lists them all. To ask whether Lolly reads or writes a format, call lolly_list_tools with a format filter, or fetch /info/capabilities.json. There is also a page for every format at /info/formats/<token>/ and one for each common conversion at /info/convert/<in>-to-<out>/.

One vocabulary across the CLI and this server

lolly_verify and the CLI's lolly validate --json answer the same question in the same words. Both report a verdict slug from one shared table (made-with-lolly, delivered-by-lolly, likely-made-with-lolly, credential-expired, credential-intact, credential-broken, no-credential) alongside resolved (the engine's semantic verdict) and the full verifier report. The slugs are frozen: an existing one is never re-pointed, and a new engine state adds a new slug, so a consumer needs a default branch and nothing else.

One predicate decides when a call needs the browser tier, shared by the CLI, the TUI and this server, keyed on a typed NEEDS_BROWSER marker rather than on the wording of an error. The open endpoint's "that needs the browser tier" refusal and the CLI's exit 3 are the same judgement.

The twenty-two tools

Discover and describe:

ToolDoes
lolly_list_toolsList or search the catalogue by text, status, category, format or capability. limit keeps the matches small.
lolly_describe_toolOne tool's input JSON Schema, formats, canvas size and examples, plus emojiSets, the emoji sets this deployment registers.

Check and inspect a document before you spend a render:

ToolDoes
lolly_validateCheck tool inputs, and Design structure, with path-specific errors and warnings. Draws nothing.
lolly_compileCompile a hydrated document without rasterising.
lolly_inspectThe semantic read model of a document, or of a file you supply, without rasterising.
lolly_measureSizes, counts and duration of a document, without rasterising.
lolly_diffA semantic diff of two compiled documents or recipe query strings.
lolly_packageA Design document as a .lolly the app reopens as the same document, with its pictures and name.
lolly_measure_textWhere a plain Design text layer's lines break and how tall it is, before it is drawn. Works on the open endpoint.
lolly_composeSlides laid out from the slide master's archetypes, as a Design document.

Explain the design system:

ToolDoes
lolly_inspect_tokensWhy a design token has its value, without rendering. Writes nothing.

Build a link or render:

ToolDoes
lolly_build_urlA shareable, editable link and a raw render URL, without rendering.
lolly_renderRender a tool to a file. Returns the bytes and the editable link.

Look at a render in its own coordinates:

ToolDoes
lolly_lookThe render with a labelled grid, or one region enlarged to read small type. For looking only, never an export.
lolly_sample_colorThe colours at points, each compared with the active design system.
lolly_trace_edgesThe edges in a render or image as polylines in document units, longest first.

On-device file utilities (bytes in, bytes out):

ToolDoes
lolly_transformRun an on-device file utility (strip-data, compress-pdf) on a file you supply.
lolly_redactDestroy regions of an image, SVG or PDF you supply.
lolly_verifyVerify a file's Content Credentials (C2PA).
lolly_rebrandRenovate a PowerPoint deck you supply into this server's design system, in stages.
lolly_readRead what a deck you supply says (.pptx, PDF or Photoshop .psd), slide by slide.
lolly_checkCheck a Design document, a .lolly or an export with every check Lolly has, in one findings list.

On an authenticated connection with a file scope, six more tools appear for importing a private file once and working on its handle: files_import, files_list, files_download, files_convert, files_report and files_delete.

What the tool arguments take
  • lolly_measure_text returns each line with its width and a nearEdge flag, the height, the scrollHeight the canvas reports and, with height, whether the box clips the text. Pass text and width (plus any of the layer's fields, or a style id to measure it as an authored layer), or a document to measure every plain text layer. The same measure as lolly measure --text --json. It reads no file.
  • lolly_package takes document (a boxes array, {boxes} or {values: {boxes}}) or toolId: "design" with inputs. assets carries the bytes of each placeholder picture, base64, by the image value that draws the picture. source is a deck whose pictures resolve user/media/<sha256> references and the photo:<12 hex> keys lolly_compose suggests; label is the name. Every other picture must be a catalog id of the server's profile, and anything unresolved is refused unless allowMissingMedia. It returns the file and a report of what was carried and read back, the same report as lolly package --json. A compiled document of another tool is packaged as before and answers {legacy: true, manifest}.
  • lolly_compose takes a spec (slides, each with an archetype and its slots) and, for from references and notes, the deck's inventory or the deck itself as source. It returns the document, a report of what each slide filled and dropped and which text slots clip (fit: "shrink" steps them down), the source wording the slides change as edits for lolly_check, and the pictures named. mode: "list" returns the archetypes; mode: "suggest" a first spec for a deck. The same composer as lolly compose --json.
  • lolly_inspect_tokens reports the effective theme choices (one per group, which fell to a default and the set order). It also lists issues such as missing references, cycles and unknown choices, plus a page of tokens with resolved values (query, type, set, issuesOnly, offset, limit). With token, it adds the authored and resolved value, the winning set and override order, the roles the document declares and a bounded trace of the tokens it reads and the tokens that read this one (depth, 8 by default). It reads this server's design system, the one lolly://design-context describes, or a tokens document you pass; selection takes a render link's _themes object and theme one theme name. Choices are a preview. The same report as the TUI's token inspector.
  • lolly_look numbers its grid in document units: for Design, the artboard pixels a layer's x, y, w and h use, so a position read off the picture can go straight into layerPatches.
  • lolly_sample_color averages each point over a small disc and gives the nearest colour token, its distance in OKLab (ΔE; about 0.02 is just noticeable) and whether that is a match.
  • lolly_trace_edges with asDesignLayers also returns each line as a Design path layer, ready for layerOperations once it has an id.
  • The three looking tools take a source the way lolly_render does, or a file you supply (PNG, JPEG, GIF, WebP or SVG, measured in its own pixels). A supplied SVG may only carry images inside itself: a reference to any other file is dropped before drawing.
  • lolly_redact takes the same instruction string a share link carries (bars=1,40,60,200,24~…), so one string can be applied to every file of an identical layout. The tool rebuilds the file and re-checks its own output; a failed check returns an error with no file attached.
  • lolly_verify returns the verdict, signer identity, edit history and embedded metadata, including any AI-generated declaration and appended-data flags. It is the same C2PA verifier as the CLI's lolly validate. The web Verify page's pixel-level reads (the Lolly Imprint, SEAL, the opt-in deep scan) are interactive and web-only. The file is checked in the server process and never stored.

Recipes

Edit a Design document by layer id

For Design, start from templateId plus an optional presetId and a small set of input overrides; lolly_describe_tool lists those starting points. The same arguments work with validate, compile, inspect, measure, URL building and render. Inspect the template once to find its stable layer ids, then send layerPatches: [{ "id": "s1title", "set": { "text": "Your title" } }] to replace content without resending layer geometry. An agent can start from a slide deck or a square poster without inventing raw coordinates.

Use layerOperations to change the layer set itself. It supports strict stable-id add, duplicate, remove, reparent and reorder operations. Reorder anchors must be siblings. Reparent keeps the layer's geometry and accepts a destination artboardId (null means the pasteboard), plus an optional destination sibling anchor. Every duplicate takes an explicit newId; duplicating a non-empty artboard also needs a complete childIds map. Removing a non-empty artboard needs cascade: true. Operations run before layerPatches, so a newly created or moved layer can be patched in the same call:

{
  "toolId": "design",
  "templateId": "slide-deck",
  "layerOperations": [
    {
      "op": "add",
      "layer": { "id": "s1-kicker", "kind": "text", "frame": "slide1", "text": "Draft" },
      "afterId": "s1title"
    }
  ],
  "layerPatches": [
    { "id": "s1-kicker", "set": { "text": "Agent-added context" } }
  ]
}

Rebrand: renovate a deck, in stages

lolly_rebrand reads a .pptx you supply and turns it into this server's design system: colours, fonts and logo snapped to the system, decoration and page furniture pulled out. It runs the same node pipeline as lolly rebrand on the CLI, so a plan made on one compiles on the other, as long as both read the same design system; otherwise compile refuses it with plan.design-system-mismatch. It runs no browser and needs neither endpoint's browser tier, so it is available wherever the MCP server runs.

On mcp.lolly.tools and lolly.tools/api/mcp, calling lolly_rebrand uploads your deck to Lolly's server. It is processed in memory for that call only and not kept. Each endpoint takes a deck of up to about 3.2 MiB, because the deck and its base64 encoding must fit one request of up to 4.5 MB (LOLLY_MCP_REQUEST_MAX_BYTES). For a larger deck, run lolly rebrand on the CLI or point an agent at a local MCP server. lolly_read, lolly_check and lolly_compose also send a deck you pass them to the server.

  • {stage: 'capabilities'} first, on a server you have not used. It states in a plain sentence where the file goes. A local server (stdio, or an HTTP server on a developer's own machine) keeps it in that process. A hosted server says that calling the tool sends the file there, processed in memory for that call with nothing kept, and gives its byte and slide limits and the request and answer sizes it takes. ocr is false on every server: this pipeline reads no text out of pictures.
  • {stage: 'plan', file} returns the review queue, the counts and the plan itself (as a JSON resource when it is too large to inline). Edit that plan rather than making a new one.
  • {stage: 'compile', file, plan} turns it into a Design document (.lolly) and, with export: 'pptx', a native PowerPoint file. A plan compiled against different bytes than it was made from is refused (plan.hash-mismatch), not reconciled.
  • {stage: 'inspect', plan} pages through a large plan's queue and slides without pulling the whole plan back.

The agent skill's Rebrand reference walks an agent through the stages and the plan's fields.

Read a deck, check a recreation

When an agent rebuilds a deck in Design rather than renovating it with lolly_rebrand, three calls carry the work:

  • Read lolly://design-context. The design brief: approved colour pairings, type per role, logos per surface, icons, the slide master and the brand's house rules.
  • Read the source. lolly_read gives the source deck's content.
  • Write the document. Compose, measure and package it (next section).
  • Check the result. lolly_check against the source or its inventory.

lolly_read takes {file} and returns the content inventory (schema content-inventory-v1) with counts per kind: text in reading order with roles and runs, speaker notes with paragraphs and line breaks kept apart, pictures by content hash with placement and crop, tables and charts as data, plus the class of every object. An inventory over 128 KB comes back as an embedded JSON resource instead of inside the structured result. Pictures come back as facts (hash, type, size, placement); media: "inline" also returns the bytes of each distinct picture, one embedded resource each. No text recognition runs, so a slide that is one picture of a slide reads as a picture (ocr is false). The same reader as lolly read --json.

lolly_check returns the same report lolly check --json writes (schema check-report-v1): five families (structure, render, brand, verify, fidelity), each ran, skipped, unavailable or failed with a reason, and one findings list. Each finding has a stable code, a severity, the layerId where there is one, the message the app shows and a fix where one is safe. Findings are data, so the call is a tool error only when the request or the file cannot be read.

The render family (clipped text, contrast and font coverage on the painted page, with the document's pictures painted) needs the browser tier. A hosted server runs it only where it has a browser and a web shell to drive: the browser image with LOLLY_WEB_BASE set. On lolly.tools/api/mcp, which has no browser, it reports unavailable and the other families still run. Verify runs with text recognition off on every server. On a hosted server the file and the source travel in one request, so pass the lolly_read inventory rather than the source deck there.

What lolly_check takes and the exit codes it reports

lolly_check takes exactly one of file (a Design document, a .lolly or an export), toolId: "design" with inputs, templateId, layerOperations and layerPatches as lolly_render takes them, or a compiled document.

Add source (a deck) or inventory (what lolly_read returned, or the whole lolly_read result it came in) to check fidelity. With either, edits (a list of { source, result, reason }) records wording you meant to change: those findings stay in the report as info, marked excepted, and are listed under fidelity.excepted, never as passed.

theme checks the document in one token theme, its linked colours resolved there. themes (a list of theme names, or "all") checks it in each in one report: the render, brand and Verify families run in each theme and their findings carry the theme they were found in, while structure and fidelity run once. A link that does not resolve in a theme is a brand.token-link.unresolved warning. Give one of theme and themes, not both.

exitCode is the code the CLI would exit with:

CodeMeans
0Clean
5Findings to review
4An error finding, or any warning with strict
3browser: "require" and no browser is available to render the document. An export has no render family, so the requirement does not apply to an export
1A family that failed, or an export with no page that could be decoded

Compose, measure and package a Design document

Between the read and the check, the agent writes the Design document. For slides, start with lolly_compose: it lays each slide out from an archetype of the design system's slide master, with the master's margins, type, furniture and bindings. Because composed slides keep their master bindings, lolly_check judges them by the master's house rules, and their PowerPoint export has real slide layouts and placeholders.

  • mode: "list" returns the archetypes and their slots.
  • mode: "suggest" with the inventory (or the deck as source) drafts a first spec.
  • The edited spec with the same inventory composes the document.

A spec gives an archetype per slide and fills its slots, by text or by from an inventory object. source on a slide carries that slide's notes. join on a slot ({from, join: ": "}) runs a heading's lines into one. cells, ground, furniture, emphasis, case, under and over cover the rest. themes: ["light", "dark"] on the spec makes one document that serves both themes: logo furniture as <id>?theme=auto, and the master's token colours and the under and over token references stored as literals plus links. furniture, emphasis and case at the top of the spec are deck defaults that each slide merges over. The schema is https://lolly.tools/schemas/design-compose-v1.schema.json, described in the agent skill's Design reference.

The result is the document, a report of what each slide filled, dropped and clipped, the edits to give lolly_check (each with a generic reason to replace) and the pictures it names. A hosted server receives a deck passed as source; pass the lolly_read inventory there instead.

For what no archetype holds (a slide's under and over rows) and for slides written by hand, rows may carry authoring keys, which all start with $: $in gives a layer's position relative to its artboard, $style sets its text by a style from the design brief (title, subtitle, body, caption, label, quote, number, attribution) or from $styles beside boxes, $points and $d draw a path in px, and $stack, $grid and $table lay out repeated rows. Every tool that takes a Design source lowers them first, to ordinary layers in global canvas coordinates with every text field written out: in inputs.boxes, in an add operation's layer and in a patch's set. They are never stored. A key that cannot be lowered is an error with the JSON pointer of the key, and lolly_check reports it as a design.authoring.invalid finding. lolly_compile and lolly_validate return any notes the lowering made under authoring. The keys are described in the agent skill's Design reference, and their schema is https://lolly.tools/schemas/design-authoring-v1.schema.json.

lolly_measure_text answers where a text box's lines break before it is placed, with the faces the canvas loads. A line marked nearEdge is within a few px of breaking another way, so widen that box rather than trust the fit. lolly_package then writes the .lolly a person opens in the app. On a hosted server the pictures and the source travel in one request, and the .lolly comes back in one answer (at most 4.4 MB on a serverless deployment), so package a large deck with lolly package on the CLI.

Redaction needs the full endpoint

lolly_redact rebuilds real pixels (a canvas for images, a page render for PDFs), which the browser-free tier cannot do. On the full endpoint it runs in the same browser path a person clicks in the app, including the tool's own export gate. On the open endpoint it returns an error saying the browser tier is not available there, rather than handing back a file that was never redacted. lolly_transform behaves the same way for any utility that rebuilds pixels; the metadata-only utilities (strip-data, compress-pdf) still run browser-free.

What a render records

The design system and colourway. A render records the design system and colourway that drew the file, in its metadata and its Content Credential, as an export from the app does. The result reads the delivered bytes back and prints the words the file carries, such as Recorded the design system: Lolly Demo · Colourway: Beach. Pass madeWith: false to lolly_render or lolly_build_url to leave the record out; the link then carries _madewith=0, so the file and the link agree.

Songs. A video or audio file that plays a rondocode song records each song in its Content Credentials: its name, a hash of its source and how Lolly's sandbox rendered the song. The result reads the delivered bytes back and states, on a Songs: line, which songs the credential records and any it does not. lolly_transform adds no credential, so its result says the file records none of its songs.

Emoji: name the set you want drawn

Emoji in a render are drawn from a set somebody chose, never from the machine the server happens to run on. lolly_render and lolly_build_url both take two optional arguments for that choice and read them the same way, so a link and the file it renders agree:

ArgumentTakes
emojiThe set: <id>@<version> (community/emoji/twemoji/color@17.0.3), its last two segments (twemoji/color@17.0.3), or a bare id when exactly one version is registered.
emojifxThe brand treatment for that artwork: original, snap, mono, duotone or influence:<1-9999> basis points, with an optional ,unprotected to treat skin tones, flags and custom symbols too. The convenience form reads the current brand; generated links also pin the exact palette in emojistyle.

emojistyle adds the exact style snapshot, including checksums, ordered fallbacks and palette. All three use the reserved URL parameters of the same name, so the same strings work in a share link, as lolly --emoji=… --emojifx=… on the CLI and here. They travel in the link either call returns, so a person who opens the link sees the artwork the agent rendered.

Call lolly_describe_tool first and read emojiSets: one row per registered set with its id@version, label, glyph count and licence. Choosing a set is choosing a licence, which is why the licence is in the list. A set nobody registers comes back as a usage error listing the ones that are, rather than a picture that arrives without the artwork you asked for.

Resources and prompts

Agents should not guess asset ids or brand colours. Alongside the tools, the server exposes read-only MCP resources. Read lolly://skill first:

ResourceContents
lolly://skillThe Lolly agent skill (Markdown): the shared workflows and design-system setup.
lolly://skill/{path}One reference or example file from that skill, such as reference/design.md.
lolly://tool/{id}/skillGuidance for one tool, matched to the tool revision this server runs, with its references and version.
lolly://design-contextThe design brief for this server's design system: resolved tokens, source evidence, coverage and brand rules, plus approved colour pairings, type per role, logos per surface, icons and their themes, media families, the slide master and machine-checkable house rules. The same brief lolly system context --json prints, with origin giving where its tokens came from: always the server's content profile and its head tokens asset, the design system lolly_check checks against. The CLI tries --file and the terminal's active system before the profile; the server reads neither. Read it before composing, and before lolly_check.
lolly://design-system/skillsThe brand skills index: the brand's own guidance per discipline, without bodies, plus the baseline Lolly generates from the design brief. Read the overall brand skill before composing. A hosted server lists public skills only.
lolly://design-system/skill/{name}One brand skill as SKILL.md, with the brand's body unchanged and frontmatter metadata giving its asset, revision and checksum.
lolly://design-system/skill/{name}/{path}A brand skill's coverage.json (what the skill says against what Lolly checks), its lolly.json or a reference file.
lolly://catalogThe full generated tool index.
lolly://assetsEvery catalog asset id with its type, name, tags and formats. Enumerate here first, so you never invent an id.
lolly://tokensThe brand's design tokens (DTCG): named colour swatches with CMYK.
lolly://tool/{id}One tool's manifest summary, input JSON Schema and examples.
lolly://tool/{id}/previewThe tool's committed catalog preview (SVG), where one exists.
lolly://asset/{id}A catalog asset (logo, palette, font) resolved to bytes.

The intended pairing: read lolly://assets once, then pass a real id to any asset-typed input in lolly_render.

The server also publishes MCP prompts, for clients that show them as slash commands or quick actions:

Alongside the MCP endpoints, a deployment can answer the canonical embed URL directly:

GET https://<host>/tool/<tool-id>.<ext>?<inputs>

This is the raw render URL lolly_build_url returns. Drop it into a README, a wiki, a Notion page or a dashboard as an <img src> and it serves real bytes, with no token. The route is per deployment, and it is live on lolly.tools.

A GET's query string ends up in the host's access logs, and a link's inputs can carry personal data, so give a hot-link URL only what you would put on a public page. Fetching one sends the server nothing but what is written in the URL: your on-device documents, sessions and uploads can never appear in one. The privacy policy says what the host keeps.

Its scope is narrow:

The route's parameters, refusals and headers are described in OpenAPI 3.1 at /openapi.json. An operator switches the route off with LOLLY_DISABLE_RENDER_GET=1, and every such URL then returns 404.

Authentication and security

The MCP server has no persistent render history. For retained render jobs and batches, retry and cancellation, governed delivery and audit records, an organisation can run Lolly Work, which has its own lw client, HTTP API, authentication and permissions; see Organisation jobs. Connecting an agent to this server does not grant access to a Lolly Work catalog or apply that organisation's policy.

Run the server yourself

The full endpoint is the one part of Lolly that runs as a server-side add-on rather than on the device. Producing the full format range means driving a headless browser against a built web shell, so it runs as a hosted service (a container or a worker), not offline or at the edge. The on-device shells, the web app, desktop, mobile and the CLI, remain the offline and air-gapped path.

You can still run the full server yourself, fully air-gapped included, as a container that ships the scoped Chromium and a prebuilt web shell. See the Build Guide and the deployment notes in services/mcp/. pnpm run mcp:http runs the long-lived HTTP service from a checkout.

On a self-hosted, authenticated HTTP gateway, an operator can return large files as private download links instead of inline base64. Files over 1 MiB that a tool returns become MCP resource_link blocks. Download the link with the same MCP bearer used for the tool request. The link expires after five minutes, and a process restart discards the download. A copied link without that bearer grants no access. Store the downloaded file, and keep temporary dt= URLs out of documents, packs and asset references.

Budgets and rate limits

The settings
  • Daily budget. LOLLY_BUDGET_CPU_SECONDS_PER_DAY and LOLLY_BUDGET_EGRESS_MB_PER_DAY set the ceilings. On a hosted server (NODE_ENV=production or LOLLY_MCP_HOSTED=1) they default to 240 seconds of CPU and 1,024 MB of data sent per UTC day. Elsewhere they are off until you set them, and 0 turns a ceiling off.
  • Rate. LOLLY_MCP_RPM sets the calls a minute per caller: 60 on an open endpoint and 120 with a token, by default.
  • PNG area. LOLLY_MCP_MAX_RASTER_PIXELS sets the PNG area cap.
  • Sizes. A hosted server reads requests up to LOLLY_MCP_REQUEST_MAX_BYTES (default 4,500,000 bytes), sends answers up to LOLLY_MCP_RESPONSE_MAX_BYTES (default 4,400,000) and fetches images through its proxy up to LOLLY_MCP_MAX_IMAGE_BYTES (default 4 MiB). Each is a whole number of bytes, and a larger answer becomes a tool error that says why. A server counts as hosted when it sets LOLLY_MCP_HOSTED=1 or NODE_ENV=production, as every container image does, or when its public origin is not a loopback address. A local server reads none of these settings and keeps its own limits: 32 MiB requests, answers of any size and 25 MiB images.
  • Store. The budget and the rate limits share one store: any Redis-compatible HTTPS REST store, set with LOLLY_RATE_LIMIT_REST_URL and LOLLY_RATE_LIMIT_REST_TOKEN.

Caching

Cache rules for public renders and the image proxy

A shared cache may hold public renders for one day, then serve them stale for up to seven more days while refreshing. Operators who need an immediate update use a release-specific cache key or purge the render's tool, engine and catalog tags. Intermediaries must keep decoded bytes intact. The origin compresses text over 1 KiB when the client accepts gzip or Brotli; compressed responses use weak ETags over the decoded representation.

The image proxy follows its upstream's cache rules, capped at one hour. Private responses, cookies, signature-like URL parameters and private redirect hops prevent shared caching. A source that gives no usable freshness is revalidated rather than assigned a new cache lifetime.

The agent relay that lets an agent join an open document runs from the same service; Run a document relay covers its setup.

Section links from the earlier guide