Files
wordcraft/docs/mcp.md
T
02e2a51bb4 LaTeX (.tex) import and export (#7) (#11)
* LaTeX (.tex) import and export (#7)

Open and save LaTeX source as a document format, next to Markdown and
HTML. Export writes a standalone article (fontenc, ulem, xcolor,
multirow, amsmath, hyperref, loaded only when used): unnumbered sections,
itemize/enumerate, quote, center/flushright, verbatim, tabular with the
document's column widths and merged cells, equations as $..$ / \[..\],
character formatting, colours and links; metadata goes to \hypersetup.
Import reads the everyday subset: preamble \title/\author, sectioning
(\part/\chapter shift levels), paragraphs, lists with labels, quote,
alignment environments, verbatim-like environments, tabular with
\multicolumn/\multirow/\cellcolor and p{} widths, inline and display
math, formatting commands and declarations, colours, links, refs,
citations, footnotes, accents, TeX ligatures and special characters.
Unknown commands keep their arguments' text; layout and definition
commands are dropped with their arguments. Nesting is depth-limited and
input is capped, so hostile files can't overflow the stack.

The flow model gains an Equation inline so equations survive LaTeX round
trips; the other text formats write their linear text as before.

Wired into open/save dispatch, the file dialog filter, Backstage Export,
the web app, CLI/MCP help, docs and the Linux desktop entry.

Fixes #7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019FczntrDNYd6DupyBdpbL5

* #7: bound LaTeX column specs and equation export (review)

- spec_widths: `*{n}{…}` repeats recurse with a depth argument and stop at
  MAX_NEST, so a deeply nested column spec no longer overflows the stack or
  exhausts memory. Column counts saturate and widths stop at MAX_COLS.
- Column specs read from the source are capped at MAX_SPEC (4096) chars.
- balanced() looks groups up in a table of closers built in one stack pass,
  so linear_to_latex no longer rescans to the end of the string for every
  unmatched `(` (quadratic: 50k took seconds). spec_widths uses it too.
- env_name restores the position when `\begin{` / `\end{` has no `}`
  within 64 chars, so the `{` is no longer lost.
- Regression tests: the 100k-deep `*{1}{` spec and a 200k-`(` equation in
  deep_nesting_is_bounded; unclosed_begin_keeps_its_brace and
  spec_widths_repeats_are_bounded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LyVbT6Vv4BwWZ4hY8973qD

* #7: linear-time labels, brackets and \begin{; no commands from the document in exports

Applies @HaringIbon's review suggestions:

1. Parser::anchor keeps at most MAX_ANCHOR_RUN (64) bookmarks in a row with
   no text between them; text() looked back past every one, so a file of
   labels imported in quadratic time. Labels separated by text are all kept.
2. linear_to_latex_at checks for the `/` of a fraction before copying the
   group out, so deeply nested matched brackets export in linear time.
3. env_name searches only the next 65 bytes for `}`, so a run of unclosed
   `\begin{` imports in linear time.
4. An equation's `\name` is written as a command only when it is a known
   symbol, function or font/spacing command (MATH_COMMANDS); anything else
   becomes `\backslash name`. Two carets are never written side by side in
   equations or anchor names, since TeX reads `^^5c` as a backslash. Compiling
   an export can no longer `\input` files or `\write18` named by the document.

Regression tests: three cases added to deep_nesting_is_bounded, plus
latex_export_carries_no_commands_from_the_document and
latex_labels_in_a_row_are_read_in_linear_time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LyVbT6Vv4BwWZ4hY8973qD

* Merge follow-ups: FTable.borderless (from #102) in the LaTeX importer; rustfmt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 06:57:01 -04:00

1.6 KiB

MCP server

WordCraft speaks the Model Context Protocol over stdio, so Claude and other agents can write and edit documents.

wordcraft-cli mcp                          # headless session (no window)
wordcraft --control 7981 &                 # or: drive the running app…
wordcraft-cli mcp --connect 127.0.0.1:7981 # …including clicks, keys and screenshots

Claude Code: claude mcp add wordcraft -- wordcraft-cli mcp.

With --connect, the bridge sends the app's control key, which it reads from WORDCRAFT_CONTROL_KEY or from the key file the app writes at start. It sends the key only to loopback addresses; see Keys.

Tools

Tool What it does
list_commands every command (filter with query) — over 270 of them
execute run one command: {command: "insert.table", params: {rows: 3, cols: 4}}
batch run several commands in order
new_document blank, sample, letter, resume, report
open_document / save_document docx, odt, rtf, html, md, tex, txt, json; save also pdf, png
type_text type at the caret (paragraphs: true splits lines into paragraphs)
select_text select the n-th occurrence of some text
get_text / inspect_document read the document back (verify without screenshots)
render_page a page as PNG
parity feature coverage
screenshot, click, key, ui_inspect app only (--connect)

Resources: wordcraft://document (inspect) and wordcraft://commands.

The acceptance test crates/mcp/src/tests.rs writes a formatted document using MCP only.