A side-by-side look at the KitchenSink4Word and LogiSheets MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose KitchenSink4Word for workplace & productivity work as a local process; choose LogiSheets for workplace & productivity as a local process. You can also run both side by side in one mcpServers block.
create_document, copy_document, manage_backups.open_workbook, save_workbook, export_xlsx.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Workplace & Productivity | Workplace & Productivity |
| Quality signal | 60/100 · Good | 67/100 · Great |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Local subprocess (stdio) |
| Auth | Not declared | Not declared |
| Pricing | Not declared | Not declared |
| Required env vars | None listed | None listed |
| Verified owner | Community listing | Community listing |
| GitHub stars | 9 | 41 |
| Directory activity | 2 views 0 installs 0 upvotes | 1 views 0 installs 0 upvotes |
| Tools | 29 | 32 |
| Full listing | View KitchenSink4Word | View LogiSheets |
create_documentCreate a new blank .docx file, optionally setting the title core
property. Refuses to overwrite an existing file (use copy_document with
overwrite=True for that). Parent directories are created automatically.
Populate the document afterward with insert_paragraphs, create_table,
define_style (academic pack), and other tools.
Template-driven builds live in the assembly pack.copy_documentCopy a document byte-for-byte, e.g. to a new DTG-stamped filename
before editing (manage_backups action='snapshot' names such a copy for
you; this tool takes an explicit dest_path). Refuses an existing
dest_path unless overwrite=True; an overwritten destination's previous
content rotates into its .ks4w-backups prev slot first, so the
overwrite is undoable via manage_backups restore.
Split/merge and multi-document work live in the assembly pack.manage_backupsManage the automatic backups under the hidden .ks4w-backups/ folder next
to each mutated document: two stable slots per document, prev (state
before the most recent mutation) and anchor (session start).
action='list': slot files with sizes and mtimes, legacy *.bak-* files,
and orphaned slot folders whose source document is gone; give file_path
for one document or directory for a folder. action='restore': overwrite
file_path with a backup; source is 'prev', 'anchor', or a legacy *.bak-*
path. The current content rotates into prev FIRST, so a restore is
itself undoable; the payload is validated before the atomic replace, and
documents open in Word are refused. action='purge': delete backups;
scope: 'legacy', 'orphans', or 'slots'. dry_run defaults to TRUE (report
only); dry_run=False deletes. Exact paths and sizes are reported either
way. action='snapshot': save a DTG-stamped permanent copy of file_path,
YYYYMMDD_HHMM_<name>.docx (an existing leading DTG is replaced, not
stacked), optional short label suffix, optional dest_dir. Snapshots are
permanent keepers: the slots rotate on every mutation, snapshots are
never auto-pruned and no purge scope touches them. Never overwrites
(collisions get a numeric suffix); the source document is not modified.get_document_infoRead a one-call document overview: paragraph/table/footnote/comment/
revision counts, sections, and package parts. Documents open in Word are
read live (same key names; live adds 'words' from Word's own
ComputeStatistics counter plus track_revisions, and omits the part
list). Read-only.diagnose_documentProduce a one-call structural health report, read-only: content-type
coverage, dangling relationships and orphan parts, field balance per
story part, footnote/endnote integrity, references to undefined styles
and numbering, content-control and bookmark sanity, duplicate revision
ids, missing image targets, broken cross-references, and a per-part size
profile. Never fails on a weird-but-openable document; every check
degrades to a reported problem, and healthy=false only for problems
that render broken or lose content in Word. The deep companion to
validate(checks=['core']). No live mode BY DESIGN: this reads the saved
package's XML, stale while Word holds unsaved changes. Close the
document first, or use com_validate_opens_clean (com-live pack) /
live get_document_info.
The full validate check battery lives in the academic pack.get_textRead body paragraphs as [{index, text, style, ...}] with effective
styles. start/end slice by paragraph index (0-based, end EXCLUSIVE);
contains filters. include_textboxes=True appends text-box content as
labeled extras, body indices untouched; textbox=true (or {"index": n})
returns ONLY text-box content, with the box_index set_textbox_text
(media-forms pack) takes; file-mode only. Open documents read live,
same shape.
Equations read via list_elements type='equations'. Read-only.
Orientation: get_document_view.find_textFind text in paragraphs and table cells; returns locations plus
context. include_textboxes=True also searches text-box content as
labeled matches; file-mode only. Pass formatting={...} (bold, font,
size_pt, color, ...) to search by EFFECTIVE formatting: resolved
through run, style chain, and defaults; query=None returns every
formatted stretch; scope 'body' or 'all'; file-mode, no regex. Plain
queries search open documents live (same shape, 500-match cap).
Read-only. To read a known range, use get_text.get_outlineList every heading with its paragraph index and level. Detects
Heading styles AND w:outlineLvl overrides (direct or style-inherited);
detected_via names which. detect_formatted=True adds a heuristic scan
for direct-formatted headings (short bold, italic or centered;
numbered captions excluded): CANDIDATES with an INFERRED level and a
confidence. Detecting nothing returns a note plus flat structure
counts. The indices feed the location object's paragraph and outline
selectors. Read-only. TOC building: academic pack.list_elementsEnumerate any collection in one call. type: tables |
images | charts | equations | bookmarks | sources | sections |
section_blocks | footnotes | endnotes | fields | reference_fields |
form_fields | content_controls | template_placeholders | index_entries |
lists | toc. Returns {type, count, items}; the ids or indices
returned are the handles the matching set_/delete_/manage_ tools
take (list then act). Highlights per type: tables reports
dimensions and the table_index other table tools take; images reports
display size and the media target; charts reports series; equations
reads math content; bookmarks excludes internal TOC bookmarks; fields
covers complex and simple fields with cached results;
reference_fields inventories Zotero, EndNote, and Mendeley fields and
flags broken pairs; form_fields and content_controls cover legacy fields
and SDTs; template_placeholders lists {{name}} and MERGEFIELD keys for
fill_template and mail_merge (assembly pack); lists groups list
paragraphs by numbering instance; toc returns TOC-family fields with
cached entries (refresh via com_refresh_fields, com-live pack, then
re-read). filter={"range": {start, end},
"name": "substring"} applies where meaningful; inapplicable filters
refuse loudly. Read-only, file mode; close documents open in Word
first.
Tools for these elements live in packs; enable_tools lists
them. To locate a string, find_text.get_workflowsRecommended tool sequences for common multi-step tasks, with a one-line
why per step. Call with no task to list the available tasks
('process-feedback', 'prepare-submission', 'format-citations',
'build-from-template', 'heavy-editing', 'live-editing', 'comment-partner',
'migrate-from-v1', 'bulk-edit');
call with task='<name>' for that task's step-by-step sequence and notes.
Pure guidance: reads nothing, changes nothing.get_server_infoReport this server's build and environment: version, the active tool
surface (tool count, approximate token bill, per-pack state), the packs
enable_tools can load, what decided the startup surface and whether it
is locked, whether the Word application tier can run here, whether path
sandboxing is on, the host OS and Python, and the update-check state.
Needs no document, opens no file, starts no Word. Paste-safe: no path,
document name, or user name, so a bug report can carry it as it stands.insert_paragraphsInsert paragraphs (items {text, style?, formatting?, heading_level?,
paragraph_format?}) at a location object (omitted = document end).
heading_level 1-9 makes it a heading (1 = outline 0).
paragraph_format takes set_paragraph_format keys; it and
inherit_format/copy_format_from (clone neighbor formatting minus
outline level) are file mode only. track records insertions by author.
Auto-backup; atomic save. Open documents edit live; a stale
text-selector target refuses: save in Word, retry. Batches:
apply_edits.open_workbookStart the workbook you will work in. Call with no arguments for a fresh, empty one; pass `path` to load an existing .xlsx from disk and work on the human's real file.
This replaces whatever workbook the session currently holds, discarding unsaved changes — so call it once at the start, not between steps. You do NOT have to call it at all: an empty workbook appears automatically the moment any other tool touches the session.
Prefer `path` over `xlsx_base64`. `xlsx_base64` exists for hosts with no shared filesystem and costs enormous context for a file of any size.save_workbookWrite the workbook to a real .xlsx file the human can open in Excel. This is how you hand your work back — do it when the task is done.
Defaults to the path it was opened from, or last saved to; pass `path` to write somewhere else. Values, formulas and the block structure are all saved.
IMPORTANT — if the human is going to work on this in Excel, pass `resolve_block_refs: true`. Formulas that read blocks are written as BLOCKREF/BLOCKREFS, which only LogiSheets understands: Excel shows the saved numbers but turns those cells into #NAME? the moment it recalculates. Resolving rewrites them as ordinary A1 references so Excel can recompute the model. Leave it off when the file is coming back here — the named form is readable and survives rows moving.
The result carries a link to the workbook rather than its bytes, so the host can offer the human the file without any of it passing through your context.export_xlsxReturn the workbook as base64-encoded .xlsx bytes.
Last resort. The bytes land in your context and cost roughly 1.4 KB of text per KB of file. Prefer `save_workbook`, which writes a real file and hands the host a link to it — the host can give the human the workbook without any of it passing through you.list_blocksOrient yourself in a workbook: every sheet, every block, and for each block the fields you can reference. One call is enough to start writing formulas.
Per block you get its ref name (BLOCKREF's first argument), its `fields` in column order (the third argument), `key_field` — the column whose values BLOCKREF matches as its second argument — and `derived_fields`, which are computed by a rule and reject writes.
Use `describe_block` when you need a field's actual rule, or the row keys and values. `next_block_start` clears everything already on the sheet, blocks and loose cell content alike, so passing it as `create_block`'s `position` will not land on top of data.
Omit `sheet` to scan the whole workbook; passing it restricts to one sheet.describe_blockReturn a block's full structure for the LLM: identity (name, sheet, position), per-field schema, and row keys in order. Every field reports both what it IS — `type`, `description`, `required`, `unique`, `default_value`, and `references` for a field pointing at another block — and what currently guards it: the `value_formula`, `validation` and `editability` templates. All of it comes from the Rust schema, the engine's authoritative source, so the answer is the same in every host and survives a save/load.
Read a field's `type` and `description` BEFORE writing a value to it: the description is where the unit, the convention, or the thing-not-to-do is written down, and it is the only record of it. A `type` of null means nobody has claimed one yet.
`analyzes` and `analyzed_by` say whether this block is a table or a conclusion drawn from one. A block with `analyzes` set is an ANALYSIS block — a total row, a set of statistics, a pivot — and its rows are its own, not the source's. Never sum an analysis block alongside the block it analyses: that counts the same numbers twice. A field with `aggregates` set is engine-computed from the source; writes to it are dropped, and you change it by changing the declaration.
Pass `include_rows: true` to additionally include current cell values as `rows[].values[fieldName]`. Off by default to save tokens — use it when the agent actually needs to inspect data, not when it only needs the shape.eval_formulaEvaluate an Excel-style formula in a private scratch cell and return the computed value. Nothing is written to user-visible cells. Returns `{type, value}` where type is one of:
- "number" — value is a JS number
- "str" — value is a string
- "bool" — value is a JS boolean
- "error" — value is the Excel error code (e.g. "#REF!", "#NAME?")
- "empty" — value is null (formula returned an empty cell)
Use for:
- Quick checks: "=SUMIFS(OrderStatus, \"amount\", \"*\")" → total
- Sanity-test a candidate template before set_field_rule
- BLOCKREF / BLOCKREFS lookups against any block in the workbook
Leading "=" is optional — it is added automatically if missing.create_blockCreate a structured block (table) on a sheet. fields[0] is the row-key column — the value BLOCKREF matches on, so it has to be unique per record; it is ordinary data you can write. Block ref name (`name`) is used as the first arg to BLOCKREF/BLOCKREFS in formulas.
Field types supported:
- 'string' / 'number' — plain text/numeric cells.
- 'boolean' — cell stores 0/1 or TRUE/FALSE; UI renders ✅/❌ if host has the widget set.
- 'enum' (+ enum_id) — cell stores variant id; UI renders dropdown if host has the widget set. Watson auto-injects a variant-whitelist validation formula on the field so out-of-set writes light up as warnings even without widget rendering. Requires a prior define_enum_set call with matching id.
Rules (value_formula / validation / editability) are set separately via set_field_rule — this call only declares structure + initial rows. Auto-creates the target sheet if missing.convert_to_blockTurn a table that already exists in ordinary cells into a block, in place, without touching its values.
This is how you adopt a workbook someone hands you. `create_block` is for new tables and refuses to write over existing data; this one takes the data as it stands and gives it a name, fields and row keys, so you can address it as (block, row_key, field) and reference it from formulas by name instead of by coordinate.
Give `position` and the counts for the DATA only, leaving out any header row. Field names come from the header row — pass `header_row`, or leave it out and the row directly above the data is used when it looks like titles. `fields` states them outright instead.
The row key is inferred: the first column whose values are all present and all different. Pass `key_field` to choose. Whatever is inferred is reported back, so check it — the key is how formulas and later edits name a row.
Converting is a one-time cost: the block and its schema survive saving and reloading, and afterwards the region behaves exactly like one created as a block.set_block_descriptionWrite what a block is for, in prose, onto the block itself. Saved in the file and returned by describe_block, so it is how the next agent — or the next you, in a later session — learns what the table means.
Say what one row represents, what any non-obvious field holds, and anything that must not be done to it. Do not restate the field names; they are already visible.
Refused when the block reserves `modifyDescription` to its owner. Pass an empty string to clear it.add_block_rowsAdd rows to an existing block. Appends at the end by default; pass after_key or before_key to insert at a position instead. Each row needs a key; values is an object keyed by field name. Fields with a value_formula are auto-materialized by the engine — don't pass them in values. Validation/editability shadows for the new rows are auto-installed by the engine at InsertRowsInBlock time, so no follow-up is needed. Also inserts the matching sheet rows (one block per sheet-row assumption — extends the sheet so downstream rows shift down).delete_block_rowsDelete rows from a block by their key. Missing keys are silently ignored. Also deletes the matching sheet rows (one block per sheet-row assumption). If you try to delete every row, the last one is kept and its cells are cleared instead — the engine doesn't allow rowCnt=0 blocks.move_block_rowReorder a block by moving one row to a new position, addressing both the row and its destination by key. Moves to the end when neither after_key nor before_key is given (same default as add_block_rows). Row order inside a block is presentation only — formulas address rows by key, so reordering never changes a single computed value. Use it to match a reading order someone expects, not to change the model.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"kitchensink4word-2": {
"command": "uvx",
"args": [
"kitchensink4word"
]
}
}
}
{
"mcpServers": {
"logisheets": {
"command": "npx",
"args": [
"-y",
"logisheets-mcp"
]
}
}
}