mcp-htmleditor
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
mcp-htmleditor
WYSIWYG HTML editor for LLM-assisted document creation. A browser editor renders the HTML file as-is in an iframe (all styles/JS intact), with lightweight in-place editing; a FastMCP server lets AI agents create, modify, and export HTML presentations and documents.
Stack
Python 3.13+, uv, FastMCP, python-pptx, BeautifulSoup4, Click
pydantic-settings for configuration, rich for logging, OpenTelemetry for tracing
No Node.js, no build step (iframe renderer + vanilla JS)
pandoc for DOCX export
stdlib
http.serverfor the HTTP server
Related MCP server: @designjs/mcp-server
Installation
Everything goes through make (uv under the hood, never pip).
System install (XDG compliant)
make installmake install performs:
Installs the CLI as a uv tool in its own isolated environment and links
mcp-htmleditorinto~/.local/bin/(ensure it is on your PATH). A legacypip install --usercopy is removed when found, because its console script used to shadow both the tool and the sources.Copies templates into
~/.config/mcp-htmleditor/templates/Creates the log dir
~/.cache/mcp-htmleditor/logs/Installs the dynamic Pi skill into
~/.pi/agent/dynamic-skills/html-editor/(then add the routing rule to~/.pi/agent/dynamic_prompt.yaml, seedynamic-skills/README.md, zero overlap with thepptx/docxskills via the word "html")
The tool environment is a snapshot: run make install again after changing the
sources. All install targets are overridable via env vars (see below).
make uninstall reverses everything.
Development
make sync # uv sync: .venv from the committed uv.lock, dev group included
make check # lint + format-check + typecheck + security + tests with coverage
make run ARGS='export pptx pres.html /tmp/out.pptx'
make run-dev ARGS='-v serve doc.html'Docker
make docker-build # image mcp-htmleditor:latest, version from the git tag
make run-up # docker compose up -d, editor on http://localhost:7842/
make run-downThe image is multi stage (python:3.13-slim), installs from uv.lock, ships pandoc,
runs as a non root user and exposes GET /health. Documents live in the ./data
volume mounted on /data. The image has no home directory, so it sets
HTMLEDITOR_STATE_DIR=/var/lib/mcp-htmleditor (history and server records, a named volume in
docker-compose.yml); without a writable state directory every save would fail.
Details in .agent_docs/docker.md.
Version
mcp-htmleditor --version # or: curl localhost:7842/healthsrc/mcp_htmleditor/version.py holds dev in the working tree; make build and
make docker-build overwrite it from git describe --tags --always --dirty.
Usage
Create from a template (recommended)
mcp-htmleditor templates # list templates: ei, carbon, medical, doc, doc-perso, doc-ei, mail, website
mcp-htmleditor new ei ma-presentation.html --serve # create + open editor
mcp-htmleditor new medical talk.html --serve # medical deck (congress, staff, teaching)Visual editor
mcp-htmleditor serve path/to/file.html # open existing file
mcp-htmleditor serve file.html --port 7842 --poll 500
mcp-htmleditor -v serve file.html # DEBUG logs (-q for errors only)
mcp-htmleditor serve file.html --host 0.0.0.0 --no-browser # remote or containerWithout --port, a free port is auto-picked (7842 first, then 7840-7849), so several
presentations can be served at once (one process per file) without colliding on the
default port — see Multiple presentations at once.
An explicit --port is used as-is and fails with a clear error if it is already taken.
Global options come before the subcommand: -v / --verbose raises the log level,
-q / --quiet keeps errors only, -V / --version prints the version.
Edit mode toggle (top-right "Édition"): in-place rich-text editing, format toolbar
on selection (bold/italic/underline/strike, superscript/subscript, align, size,
color), insert image (local file picker or drag-drop, embedded as base64), insert
table, slide insert/delete (presentation mode) with a template-aware picker (5 layouts for
the carbon and ei charters, 16 for medical), and
document block insert before/after (document mode) via "+ Bloc avant / Bloc après"
pickers (title, subtitle, h1-h5, paragraph, table, list). Drag-and-drop editing:
reorder top-level document blocks with a left-side grip handle (DOM order = visual
order, no attribute added), and move arch-diagram nodes with the mouse (position
written as readable data-x/data-y percentages plus inline left/top in %).
Declarative arch-diagrams (with at least one arch-row) also support add/remove
node and add-edge via right-click (see skill/topics/arch-diagram.md § Édition à
la souris): every structural change saves then calls the server's arch-layout
engine, the browser never computes a position itself.
Text width bar: pointing at a text block shows a thin bar on its right edge. Drag it to
set the text's width live (steps of 10 px, Alt for single pixels, 80 to 1280 px); focus it
and use the arrow keys (Shift for 50 px); double click it to go back to the template's
width. Letting go saves one declaration, max-width:Npx, in the text's inline style
(other declarations kept), as a single undo step, and nothing of the bar reaches the
file. Agents read it as a plain declaration. The PPTX and DOCX exports ignore it (the
text keeps its block's width, see BL-0033).
Undo/redo (Ctrl+Z / Cmd+Z, Ctrl+Shift+Z / Cmd+Shift+Z or Ctrl+Y), active in edit mode:
covers every structural mutation above (node move, node/edge/table/gantt-task
add/remove, slide insert/delete, document block insert/reorder) with a 50-deep
undo/redo stack. Plain text edits inside a text zone keep using the browser's own
native undo, untouched by this feature — see .agent_docs/html-conventions.md §
Undo / redo for the exact scope and the native-vs-structural interaction.
Presenting
Open a deck with the Présenter button (or press F) to show it full screen; the arrow keys, space, Home and End move between slides and Escape leaves. While presenting full screen, press L or start with the Pointeur button beside the present button to turn the mouse into a red dot; a click rings the box under it (a tile, a table, else the headline or paragraph); the pointer starts off at every presentation and nothing of it is ever written to the file. It works the same in a deck opened directly and in the editor (not while edit mode is on).
Saving, conflicts and history
The editor saves the whole document. Each save carries the modification time the browser last saw; if the file changed on disk since (an agent wrote it), the server answers 409 and the page offers Reload (show the disk version, drop the unsaved edits) or Keep mine (overwrite). Every write is atomic (temporary file, then rename): the file holds the complete old or the complete new content, and no other file is left next to it. The check and the replace are serialized per process only, not against an agent writing the file directly or against another process.
Before a browser save, a patch, a Keep mine or a restore changes a file's bytes, the previous content
is kept in the history (20 versions per file, oldest dropped first). Each version is {id, bytes, reason},
reason one of browser-save, keep-mine, patch, restore. A write that changes nothing keeps nothing.
mcp-htmleditor history deck.html # <id> <bytes> bytes <reason>, newest first
mcp-htmleditor restore deck.html <id> # put it back; the replaced content is kept as a new versionThe same operations are the MCP tools history(file) and restore(file, id). Templates under templates/
are never modified by a save or a restore.
The "modification in progress" flag (update_start) expires by itself 120 seconds after the last
update_start, so a crashed agent never leaves the overlay up. Flags, server records and the history live in
the state directory (HTMLEDITOR_STATE_DIR, default $XDG_STATE_HOME/mcp-htmleditor, else
~/.local/state/mcp-htmleditor), never next to the edited file; .mcp_state.json files left by older
versions are ignored and can be deleted.
Request loop
The person asks the agent for a change from the editor, and the agent reads, does and closes it.
Ask AI (toolbar button) opens a panel: describe the change, the current slide is attached, Queue request stores it. Up to 50 requests can be pending, 4 000 characters each.
The side list Requests (n) shows every request of the open file with its status (
pending,done,skipped); a request can be deleted from the list.ncounts the pending ones.The agent works with the tools
list_requests(file),complete_request(file, id, status, reason=None)andwrite_review(file, suggestions), or the same operations by command:
mcp-htmleditor requests deck.html # <id> <status> <slide> <title>, oldest first
mcp-htmleditor request-done deck.html R-0001 # mark done
mcp-htmleditor request-skip deck.html R-0002 --reason "Out of scope" # a reason is required
mcp-htmleditor review deck.html # <id> <slide> <title> of each suggestion
mcp-htmleditor review deck.html --write review.json # replace the review (- reads stdin, at most 6)A tool error carries the text of the command line error: line without its prefix.
Where the state lives: <state_dir>/files/<key>/requests/record.json (plus record.lock), where <state_dir> is
HTMLEDITOR_STATE_DIR (see Environment variables) and <key> identifies the file by its real path. Nothing is
created next to the edited file, and two files never share requests. Removing the state directory removes the
requests and the review.
MCP server (stdio)
mcp-htmleditor mcpConnect your LLM agent via stdin/stdout (e.g. Claude Desktop, Cursor, pi).
Skill content
mcp-htmleditor skill # the core: rules, workflow, tools and the topic index
mcp-htmleditor skill --type <topic> # one of the 15 topics (arch-qa also prints the resolved checklist)
mcp-htmleditor skill --all # the core then every topic
mcp-htmleditor skill-audit [--json] # sizes, budgets, tool list and stale statements; exit 0 when cleanThe skill is progressive: the core (skill/core.md, at most 7000 bytes) is read first,
then the agent loads only the topics its task needs (skill/topics/<topic>.md, at most
8000 bytes each). skill-audit prints one line per item (core, then the 15 topics in index
order), one PROBLEM line per issue, and skill-audit: OK or skill-audit: FAIL (N problems).
The same content is available to agents through the get_skill MCP tool. The previous
monolithic skill is kept for reference in docs/skill-archive/.
The dynamic Pi skill triggers on "html powerpoint", "html edition", "html doc" and
instructs the agent to run mcp-htmleditor skill and then mcp-htmleditor skill --type <topic>
for what the task needs, keeping the skill content in sync with the installed tool. Routing is disjoint from the native pptx/docx
skills (the word "html" is the exclusive discriminant). See dynamic-skills/README.md.
Export
mcp-htmleditor export pptx input.html output.pptx
mcp-htmleditor export docx input.html output.docxVisual render
See a slide as a picture and find layout problems without opening the editor. It needs Chromium,
which is an optional extra: make install installs the render extra (uv sync --extra render
does the same in a checkout), then make install-browser downloads the browser. Without it the
command stops with Rendering needs Chromium. Install it with: make install-browser. The Docker
image has no browser, so render is not available in the container.
mcp-htmleditor render pres.html # first slide, 480x270 PNG
mcp-htmleditor render pres.html --slide slide-3 --scale 1 # 960x540
mcp-htmleditor render pres.html --slide slide-3 --report # picture plus its layout problems
mcp-htmleditor render pres.html --report-only # every slide, no picture
mcp-htmleditor render pres.html --report-only --json # one compact JSON objectOption | Effect |
| Id of the slide to render (default: the first). An unknown id lists the valid ids. |
| Scale of the picture, 0.1 to 1 (default 0.5). |
| Also print the layout problems of the rendered slide. |
| Print the layout problems of every slide and write no picture. Cannot be combined with |
| Print the result as one compact JSON object only. |
The picture is written to <temporary directory>/htmleditor-render/<file stem>-<slide id>.png and
the command prints Rendered <slide id> to <path> (<width>x<height>). After a successful render,
older pictures of the same presentation's other slides are removed; pictures of other files stay.
The HTML file is never modified. A --report-only run of a 40 slide presentation finishes in under 30 seconds.
With --report or --report-only each problem prints as <kind> <slide>: <message>, in slide
order, then a final line report: OK, report: 1 problem or report: N problems.
Kind | Message |
|
|
|
|
|
|
|
|
|
|
Exit statuses: 0 nothing to report, 1 at least one problem found or an error (error: <message>
on stderr), 2 conflicting options (Error: --report-only cannot be used with --slide, --scale or --report).
The MCP tool render(file, slide=None, scale=0.5, report=False, report_only=False) does the same
and returns {ok, path, width, height, problems} with problems as a list of {kind, slide, message}.
Read, check and edit a file without touching its images
mcp-htmleditor outline input.html # JSON: charter, type, slides, headings
mcp-htmleditor get-slide input.html slide-3 # HTML of one slide, large images as data:ref:img-N
mcp-htmleditor patch input.html --ops ops.json [--dry-run] # atomic slide operations, counters renumbered
mcp-htmleditor lint input.html [--json] # structure and charter rules, exit 1 on an errorThe seven agent operations
Each one is a command and an MCP tool with the same result and the same error text; the tool error is the
command line error line without its error: prefix.
Operation | Command | Tool |
Read the structure |
|
|
Read one slide |
|
|
Edit slides |
|
|
Check a file |
|
|
List templates |
|
|
Create a file |
|
|
Export |
|
|
new and export errors are now English error: ... lines on the error output with exit status 1, for
example error: File already exists: a.html or error: Unknown export format "foo". Valid formats: pptx, docx.
new_file never starts the server or opens a browser: call start_server to edit the created file.
Architecture diagram layout
mcp-htmleditor arch-layout input.html [--diagram-id ID]Computes and writes the positions of every declarative architecture diagram
(data-type="arch-diagram" with at least one arch-row child) in the file: the LLM
authors topology only (rows, nodes, edges, optional lanes), never % coordinates, and
this command derives them deterministically — no collision between nodes, straight
connectors on aligned columns, labels centered on their segment, lane boxes sized from
the union of the rows they declare. Diagrams with no arch-row (legacy, hand authored
data-x/data-y) are left untouched, so both formats coexist. See
skill/topics/arch-diagram.md.
The PPTX export writes one 16:9 slide (13.333 x 7.5 in) per element carrying
data-type="slide", with a fallback on article.slide for older templates. The
navigation shell, <script> and <style> are never exported. It detects the
charter of the document (Euro-Information, IBM Carbon, medical, generic), draws the slide
chrome (frames, footers, logo ring, header rule), then flows the content: text at
the typographic scale of the template, tile grids, callouts, native tables with
colspan / rowspan merges, real Gantt bars, diagram nodes as autoshapes with
their connectors, annotated images with the annotations placed in the image frame.
Base64 images are embedded and relative paths resolve against the HTML file. The
command prints the slide count, lists every skipped item and exits non zero when
no slide could be written. Full breakdown of what is faithful, approximated or
lost: skill/topics/export.md.
Architecture diagram QA
mcp-htmleditor arch-checklist # print the resolved checklist + where it livesAfter every arch-layout run, the agent workflow spawns a dedicated review
sub-agent that checks the rendered diagram against this checklist (screenshot,
compare, fix, re-layout, bounded to 2 passes) so the main conversation is not
polluted by iteration: see skill/topics/arch-qa.md. The checklist itself
lives at ~/.config/mcp-htmleditor/arch-checks/arch-diagram-checklist.md
(seeded once by make install, never overwritten on reinstall): add or change
a control by editing that file directly, no code change or reinstall needed. A checklist
seeded by an earlier install keeps its size and content: skill --type arch-qa prints the
topic followed by your checklist when one exists, the shipped one otherwise.
The DOCX export carries the document charter into Word. The charter is read from
data-doc-template on the document <article> (perso, ei), a matching
reference.docx is generated from pandoc's own default (fonts, heading sizes and
colours, underlines, table header fill) and cached in
~/.cache/mcp-htmleditor/reference/. A document without that attribute keeps the
default pandoc styles. The title is emitted once (Word Title, then Subtitle),
not duplicated as a Heading1. SVG figures and pandoc warnings are reported:
figures must be PNG, never SVG.
MCP Tools
Tool | Description |
| Start HTTP server + open browser. Idempotent. Without |
| Stop the HTTP server: its port is released at once, idle connections are closed, a request already received is answered. |
| Current state: file, port, pid, mtime, running. |
| Switch to a different HTML file. |
| Signal modification start (shows overlay in browser). |
| Signal modification complete (browser reloads). |
| Return the skill core, or one topic when |
| Compute and write positions for every declarative architecture diagram in |
| Structure of a file (charter, type, slides, headings) as a JSON object, without any image data. Same object as |
| HTML of one slide, every image with a data address over 1 000 characters replaced by |
| Apply validated, atomic, self renumbering slide operations ( |
| Check a file against the structure and charter rules; returns |
| The template keys with their description, |
| Create a file from a template key; refuses an existing target; does not serve it. Returns |
| Export a presentation to |
| List the saved previous versions of a file, newest first: |
| Put a previous version back by id; the replaced content is kept in the history. Returns |
| The requests the person queued in the editor, oldest first: |
| Mark a request |
| Replace the AI suggestions shown in the editor (at most 6). Returns |
| PNG of one slide plus, on request, its layout problems (see Visual render). Needs Chromium. Returns |
Multiple presentations at once
Each mcp-htmleditor serve <file> (CLI) or mcp-htmleditor mcp (one per agent session) is
an independent process. Without an explicit port, it tries 7842 then scans 7840-7849 for a
free one, so up to 10 presentations can coexist (one file each) without any manual port
juggling. Within a single agent session (one mcp-htmleditor mcp process), only one
server runs at a time: calling start_server on a second file switches the served file
(use open_file for that) rather than opening a second server — run a separate agent
session per presentation if you need several displayed simultaneously.
Templates
Templates are resolved in priority order:
HTMLEDITOR_TEMPLATES_DIR(env override)~/.config/mcp-htmleditor/templates/(installed bymake install)<repo>/templates/(development / bundled fallback)
templates/
├── bootstrap/ starters copied by `new`
│ ├── slides-ei-empty.html key: ei (Euro-Information)
│ ├── slides-empty.html key: carbon (IBM Carbon)
│ ├── slides-medical-empty.html key: medical (medical deck, single source of the charter CSS)
│ ├── document-empty.html key: doc (Word-like document)
│ ├── document-perso-empty.html key: doc-perso (Perso charter, Arial)
│ ├── document-ei-empty.html key: doc-ei (Euro-Information, Segoe UI)
│ ├── mail-empty.html key: mail (HTML email, light Carbon charter)
│ └── website-empty.html key: website (Carbon website: nav, hero, tabs, cards, footer)
└── reference/ rich examples to clone (read-only via server)
├── slides/
│ ├── euro-information.html EI: title + agenda + content, embedded logos (CSS source of the `ei` bootstrap)
│ ├── example-ei-complete.html EI: 9 slides, gantt, arch diagram, table, annotated image
│ ├── ibm-carbon.html IBM Carbon: 9 slides, all components
│ ├── medical.html medical charter catalogue, one slide per `data-slide-type` (16)
│ ├── example-medical-complete.html medical: full talk, dark imaging slides, clinical case, bibliography
│ ├── presentation-standard.html
│ └── roadmap-one-pager.html
├── documents/
│ ├── report-standard.html generic standard report
│ ├── perso.html Perso charter: title/subtitle + h1-h5 + table + list
│ ├── euro-information.html EI document: blue header + logo, blue headings
│ └── mail-carbon-example.html rich HTML email example, light Carbon charter, inline CSS
└── websites/
└── ibm-carbon.html full component catalogue: nav, tabs, cards, footer variantsAdd your own templates by dropping files into ~/.config/mcp-htmleditor/templates/
or by committing to templates/ in this repo.
bootstrap/slides-ei-empty.html is generated, not hand written: it is derived from
reference/slides/euro-information.html by tools/gen_ei_bootstrap.py (make bootstrap-ei),
which keeps the reference as the single source of the EI charter CSS, trims the deck down to
the title slide, and copies the EI chevrons data URI onto <html data-asset-chevrons> so a
slide inserted into a brand new file still gets its footer logo. Edit the reference, then
run make bootstrap-ei; never patch the bootstrap directly.
make check-generated verifies that both generated families (the EI bootstrap and the two
medical reference decks) are fresh. It runs --check on both tools, prints one
<path>: fresh or stale (first difference at line N); run: make <target> line per file,
fails when any is stale or missing, and writes nothing. It is the first step of make check.
The medical charter works the other way round: bootstrap/slides-medical-empty.html is the
single source of its CSS, and the two reference decks (reference/slides/medical.html,
reference/slides/example-medical-complete.html) embed an exact copy so they open on their
own. Edit the bootstrap <style>, then run make sync-medical-css. That charter targets
medical presentations (congress, staff meeting, teaching, interventional pulmonology): teal
#159984 plus orange #EE5A02, Trebuchet MS / Arial, grey top band and two segment rule
under the title, bibliographic source band in the footer, dark surface for imaging slides,
and 16 insertable slide layouts (the other slide charters expose 5). Every third party image
must carry a source line and every patient image must be anonymized: see
skill/topics/medical.md.
HTML data-types
| Description |
| Presentation slide section |
| Gantt chart container |
| Single Gantt task bar |
| Architecture diagram container |
| Architecture diagram node |
| Declarative layout row (input to |
| Declarative layout lane/swimlane (input to |
| Image annotation callout |
| Image + annotations container |
| HTML table |
| Word-like document article |
Environment variables
Every variable is read through the Settings class (pydantic-settings,
src/mcp_htmleditor/config.py). A local .env file is loaded automatically; see
.env.example.
Variable | Default | Description |
|
| HTTP bind address ( |
|
| Preferred HTTP port, tried first before auto-picking a free one in 7840-7849 |
|
| Browser polling interval in ms |
|
| Templates directory |
|
| Arch-diagram QA checklist directory (edit in place to add/change a control, see |
|
| File history and server records (set to a writable path in a container) |
|
| Cache base (logs, generated |
|
| Log directory ( |
|
| CLI install target |
| unset | OTLP/HTTP endpoint for spans; unset means local JSONL |
| unset | Sent as |
|
| Base for config |
|
| Base for cache |
A malformed HTMLEDITOR_PORT or HTMLEDITOR_POLL_INTERVAL falls back to its default
instead of aborting; a blank path variable means "unset", not the current directory.
Security model
The preview server is for one local user. It has no authentication, so it only answers requests it can trust as coming from its own editor page:
the
Hostname must belocalhost,127.0.0.1,[::1]or the host given with--host/HTMLEDITOR_HOST(never0.0.0.0); any port is accepted, so a published container port such as-p 7999:7842works;a request carrying an
Originheader must come from that samehttp://<host>:<port>;no CORS header is ever sent, preflight
OPTIONSanswers405, and saves must beContent-Type: application/json.
Anything else answers 403 {"error": "forbidden host"}, 403 {"error": "forbidden origin"} or
415 {"error": "unsupported content type"}, and is logged as a warning. GET /health stays open
for health checks. Limitation: in a container bound to 0.0.0.0, a client on the network that sets
Host: localhost is not stopped; do not publish the port beyond the machine (tracked as BL-0019).
The server also protects itself from malformed or stalled requests. A request with a duplicated or
ambiguous Host, Origin, Content-Length or Content-Type header, a Transfer-Encoding header or a folded
header answers 400. A client that stays silent for 30 seconds while the server waits for the request line, a
header or the body, or that takes more than 5 minutes to send one request, answers 408 {"error": "Request timeout"} and is disconnected; a connection that never sends a byte is closed without an answer. A
/arch-layout request above 1 MiB answers 413. Text or file names that cannot be encoded are refused with one
line instead of a traceback. Every refusal is logged as one Refused <METHOD> <path>: <error> warning.
Logs and tracing
File | Content |
| Application log, rotating (2 MB, 3 backups) |
| One JSON object per finished span (JSONL) |
Console logs go to stderr (stdout carries the MCP protocol and the CLI output) and every
line carries its own timestamp. Spans are named category.operation: mcp.* for tool
calls, export.pptx / export.docx with slide count, charter and duration,
tool.pandoc for pandoc runs, file.write for file mutations. Document content,
prompts and credentials are never traced. Details in .agent_docs/observability.md.
Project structure
src/mcp_htmleditor/
├── cli.py CLI: templates, new, serve, skill, skill-audit, mcp, export, outline, get-slide, patch, lint (-v/-q, --version)
├── config.py Settings (pydantic-settings): XDG paths, ports, OTel
├── logging_config.py setup_logging(): rich console on stderr + rotating file
├── tracing.py configure_tracing(), trace_span(), JSONL span exporter
├── version.py __version__, written at build time from the git tag
├── templates.py template registry + search-path resolution
├── skill_content.py assembles `mcp-htmleditor skill` output (core, topics, --all)
├── skill_audit.py `mcp-htmleditor skill-audit`: sizes, budgets, tool list, stale statements
├── arch_layout.py declarative arch-diagram layout engine (rows/nodes/edges/lanes → %)
├── mcp_server.py FastMCP server with 21 tools
├── http_server.py stdlib HTTP server (routes /, /static, /content, /status, /health)
├── fileio.py atomic_write: the one writer (temp file, rename, history snapshot)
├── history.py HistoryStore, list_versions, restore_version (history and restore)
├── state.py singleton state + records in the state directory
├── export/
│ ├── charters.py REGISTRY of CharterSpec: the eight charters (templates, detectors, DOCX keys)
│ ├── to_pptx.py HTML to PPTX entry: to_pptx, find_slides, ExportReport re-export, composed _SlideBuilder
│ ├── pptx_model.py shared model: ExportReport, block types, _BuilderBase (mixin base class)
│ ├── pptx_text.py text and paragraph rendering, rectangle and textbox primitives
│ ├── pptx_images.py annotated images, images and picture primitives
│ ├── pptx_arch.py arch diagram renderer (nodes, edges, tips, badges, labels)
│ ├── pptx_gantt.py Gantt renderer, inline Gantt detection
│ ├── pptx_blocks.py block measurement and renderers (grid, panel, rule, hbar, table)
│ ├── pptx_flow.py block flow and flex row logic
│ ├── pptx_chrome.py per charter chrome (EI, Carbon, medical, generic)
│ ├── pptx_style.py Box geometry, CSS/colour parsing, themes, typographic scale
│ ├── pptx_components.py table grid (spans), Gantt maths, low level pptx helpers
│ ├── to_docx.py HTML to DOCX via pandoc (single title, charter, diagnostics)
│ ├── reference_docx.py generates/caches a charter reference.docx for pandoc
│ ├── docx_header_footer.py repeated Word header/footer parts of a charter
│ └── docx_assets.py base64 assets embedded in the reference documents
└── static/
├── editor.html iframe shell + toolbar
├── editor-undo.js undo and redo snapshots
├── editor-edit.js edit mode injection and removal
├── editor-format.js format bar, insert bar, image embed, table and link insert
├── editor-menus.js context menus, table row and column actions, arch node actions
├── editor-save.js client side save
├── editor-slides.js slide picker, insert, delete, renumbering
├── editor-blocks.js document block picker and insert
├── editor-drag.js drag-reorder of document blocks, mouse move of arch nodes
├── editor.js state, start up, polling (loaded last)
├── slide-layouts.js per-template slide layouts (carbon / ei / medical)
├── doc-blocks.js document block definitions (title, subtitle, h1-h5, paragraph, table, list)
└── editor.css toolbar, overlay, picker styles
templates/ versioned templates (bootstrap + reference)
tests/ pytest suite (see .agent_docs/testing.md)
tools/ maintenance scripts
gen_ei_bootstrap.py regenerate the EI bootstrap from the EI reference
check_ei_insert.py browser check of EI slide insertion (logo + numbering)
sync_medical_css.py propagate the medical charter CSS to the reference decks
gen_medical_placeholders.py synthetic placeholder images for the medical decks
skill/ core.md, topics/ (15), checks/ (served by `mcp-htmleditor skill`)
docs/skill-archive/ previous monolithic skill, reference only
dynamic-skills/ dynamic Pi skill + routing doc (installed to ~/.pi/agent/dynamic-skills)
.agent_docs/ detailed docs for AI agents (python, makefile, architecture, ...)
Dockerfile, docker-compose.yml, docker-compose.prod.yml
Makefile single entry point for every operationDocumentation
AGENTS.md: compact index for AI agents (commands, conventions, doc index).agent_docs/python.md: Python standards and documented deviations.agent_docs/makefile.md: every make target and install override.agent_docs/architecture.md: module map, save path, export pipeline.agent_docs/observability.md: logs, span inventory, OTLP export.agent_docs/testing.md: test map, coverage, export regression set.agent_docs/docker.md: image, compose, container checks.agent_docs/html-conventions.md: invariants the exporters rely on
How it works
The HTTP server serves the editor shell at
http://localhost:<port>/(7842 if free, otherwise an auto-picked free port in 7840-7849, or whatever--portwas given explicitly)The target HTML file is rendered as-is inside an iframe (
GET /content-frame)In edit mode, editable zones become contenteditable; saves go via
POST /content(the server strips editor artifacts before writing, keeping the file clean)The browser polls
GET /statuseverypoll_intervalmsIf the file mtime changes (LLM wrote to it), the iframe reloads
update_start()/update_end()show/hide a "modification en cours" overlay
Skill documentation
Run mcp-htmleditor skill for the core, then mcp-htmleditor skill --type <topic> for a
topic (--all prints everything). Source files live in skill/core.md and skill/topics/. The dynamic Pi skill dynamic-skills/html-editor/SKILL.md (installed by make install-skill into ~/.pi/agent/dynamic-skills/html-editor/)
triggers on "html powerpoint" / "html edition" / "html doc" and defers to the CLI.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Online office suite for AI agents: edit docs, sheets, slides and PDFs in browser or via MCP.
MCP-native collaborative markdown editor with real-time AI document editing
Agent-native website builder: create, edit, and host websites via MCP
Create, edit, preview, publish, and manage web pages from MCP-capable AI clients.
Related MCP Servers
- AlicenseAqualityNot gradedmaintenanceAn MCP server that enables AI agents to instantly convert Markdown into beautiful, ready-to-deliver Word, PDF, HTML with sidebars, and Slideshow documents, bridging the "last mile" of AI content generation.728 npm1-
- AlicenseNot gradedqualityDmaintenanceMCP server that enables AI coding agents to read and write to a local-first HTML/CSS design canvas, bridging visual design and code generation.1MIT
- AlicenseNot gradedqualityFmaintenanceSelf-hosted presentation tool that lets LLMs create and publish interactive HTML presentations via MCP, with design guides, sandboxed documents, and shareable links.2 npmMIT
- AlicenseNot gradedqualityDmaintenanceCreate, edit, inspect, and publish collaborative HTML documents from AI agents through the HTML Docs MCP server.9 npm5MIT