npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@betteroffice/agents

v0.3.0

Published

Search, inspect, propose, preview, and export Office document edits through typed tools and MCP.

Readme

@betteroffice/agents

Search, read, edit and preview DOCX, XLSX and PPTX files from an agent, through a TypeScript SDK or the betteroffice-mcp server. Agents work on large files the way they work on source code: grep, read a small region, make an exact edit, check the result, export.

No model provider, API key or agent framework is required.

Install

bun add @betteroffice/agents

Requires Node.js 22 or later.

Usage

MCP server

bunx --package @betteroffice/agents betteroffice-mcp --root /absolute/path/to/documents

The server speaks MCP over stdin/stdout, so connect it from an MCP client. For example, with Codex:

codex mcp add betteroffice -- /absolute/path/to/node_modules/.bin/betteroffice-mcp --root /absolute/path/to/documents

| flag | description | | --- | --- | | --root <directory> | Required. Every file path is resolved against it, including symlinks. | | --read-only | Exposes only the inspection tools: no proposals, edits, acceptance or export. |

Exports always write a new file and never overwrite an existing one. Path checks are not an operating-system sandbox, so use a workspace directory you control.

SDK

import { readFile, writeFile } from 'node:fs/promises';
import { openDocx } from '@betteroffice/agents';
import { renderDocxPage } from '@betteroffice/agents/render';

const doc = await openDocx(await readFile('report.docx'), {
  name: 'report.docx',
  renderer: renderDocxPage,
});

try {
  const [hit] = doc.grep({ query: '€4.2 million' }).matches;
  if (!hit) throw new Error('Revenue text not found');
  const proposal = doc.propose({
    author: 'report-agent',
    note: 'Update the executive summary revenue',
    edits: [{ match: hit.match, newText: '€5.1 million' }],
  });

  const preview = await doc.render(1, proposal.id);
  await writeFile('preview.png', preview.png);
  console.log(doc.review(proposal.id), await doc.verify(proposal.id));
  await writeFile('report-revised.docx', await doc.export(proposal.id), { flag: 'wx' });
} finally {
  doc.close();
}

Exporting a pending proposal leaves the document unchanged. To apply it, call await doc.accept(proposal.id) and then doc.export() without an ID. For Word tracked changes, call doc.accept(id, { tracked: true, date: '2026-09-27T12:00:00Z' }).

Entry points

| export | description | | --- | --- | | openDocx(bytes, { name?, renderer? }) | Opens a DOCX file in its own session. | | attachDocx(session, options?) | Wraps a live editor's YrsSession. Closing the adapter leaves the session open. | | openXlsx(bytes, { name?, wasm? }) | Opens an XLSX workbook: cell reads and cell proposals. | | attachXlsx(handle, options?) | Wraps a live WorkbookHandle. | | openPptx(bytes, { name?, wasm? }) | Opens a PPTX presentation: outline, slide reads, text proposals, atomic edit batches and preview. | | attachPptx(handle, options?) | Wraps a live PresentationHandle. | | renderDocxPage (from @betteroffice/agents/render) | Node canvas renderer for DOCX page previews. | | renderPptxSlide | Node canvas renderer for PPTX slide previews. | | createOfficeMcpServer({ root, renderer?, readOnly? }) (from @betteroffice/agents/mcp) | The MCP server, for hosts that connect their own transport. |

DOCX and PPTX documents stage text edits with grep, read and propose; workbooks use readCells and proposeCells instead. All adapters share review, accept, reject, verify, export and close. openXlsx and openPptx load the bundled WebAssembly in Node; in a browser, pass the module bytes as wasm. DOCX previews in a browser need a custom DocumentRenderer instead of the Node canvas renderer.

MCP tools

| tool | purpose | | --- | --- | | office_files | List DOCX, XLSX and PPTX files, subdirectories and open document IDs. | | office_open | Open a file and return its document ID, format and capabilities. | | office_outline | Page through DOCX paragraphs, PPTX slide paragraphs or XLSX sheets. | | office_grep | Literal search with context; returns match IDs or cell handles. | | office_read | Read a paragraph or a cell field; DOCX reads include run formatting. | | office_cells | XLSX: read an A1 range with values, formulas and cell handles. | | office_propose | DOCX/PPTX: stage 1-32 text replacements as { match, newText }. | | office_propose_cells | XLSX: stage 1-32 whole-cell inputs or formulas. | | office_review | List proposals or show one proposal's before/after changes. | | office_accept | Apply a proposal as one undo step; DOCX can keep it as tracked changes. | | office_reject | Discard a pending proposal. | | office_render | DOCX: PNG of a current or proposed page. | | office_verify | Save and reopen in memory; reports which checks ran. | | office_export | Write accepted edits or a pending proposal to a new file. | | office_close | Release an open document and its proposals. | | docx_edit | DOCX: atomic direct edits of paragraph styles, bold/italic/underline, paragraphs, table rows, cell merges, comments (add, reply, resolve or reopen) and page setup with insert_section_break and set_section; runFormat: 'anchor' copies the first plain run on insertion. | | xlsx_outline, xlsx_read_range | XLSX: sheets, used ranges, tables and defined names; range reads with a version. | | xlsx_write_range, xlsx_format, xlsx_sort | XLSX: versioned value and formula writes, formatting and sorting. | | xlsx_list_defined_names, xlsx_edit_defined_names | XLSX: list complete names with pagination; set or delete workbook and sheet scoped names atomically, including hidden flags. | | xlsx_edit_grid, xlsx_edit_sheets | XLSX: insert or delete rows and columns; add, rename, delete or move sheets. | | xlsx_set_auto_filter | XLSX: set, extend or remove a sheet's filter buttons; table filters are refused. | | xlsx_list_charts, xlsx_read_chart_text, xlsx_edit_charts | XLSX: inspect charts on worksheets and chartsheets and page through complete titles, names and references; replace or remove titles and edit series names, category refs and value refs atomically. | | xlsx_list_conditional_formats, xlsx_read_conditional_format_formula, xlsx_edit_conditional_formats | XLSX: list bounded rule previews and page through complete formulas; add or delete cellIs and expression rules with a differential format. | | xlsx_find_replace, xlsx_preview | XLSX: find and replace displayed text; PNG of a range. | | pptx_outline, pptx_read_slide | PPTX: slides, layouts and shapes; shape tree, paragraphs, runs and notes. office_outline with story: 'comments' lists discussions; office_read reads full comment text. | | pptx_edit, pptx_preview | PPTX: atomic text, slide, layout mapping, shape, image, notes, comment (add_comment, reply_comment, resolve_comment) and table structure edits (insert_table_row, delete_table_row, insert_table_column, delete_table_column); PNG of a slide. |

Edits and references

  • References (ref, match, revision, version) belong to the open session. Reopening a file creates new ones. Page, slide and paragraph numbers are one-based; text offsets are zero-based.
  • DOCX and PPTX search is literal and case-insensitive by default. XLSX office_grep searches displayed values case-sensitively.
  • A proposal holds 1-32 separate replacements and is validated on a temporary copy before one atomic update. A changed target makes it stale (STALE_TARGET): read again and propose again.
  • New text takes the formatting of the first replaced character. To keep several styles in a paragraph, replace the individual spans instead of the whole paragraph.
  • Paragraph breaks, inline embeds and existing tracked changes are protected from text proposals.
  • A committed XLSX or PPTX edit invalidates all outstanding handles, cursors and proposals for that file.
  • Results are paginated (nextCursor, nextOffset, nextStart). A page of results is never an exhaustive search.

Limits

  • Reads return at most 16,000 UTF-16 units and 100 formatting runs; lists return at most 100 items.
  • The MCP server opens at most 10 files of up to 64 MiB each and keeps at most 64 proposals per document. All state is in memory: export before closing the client.
  • MCP messages over stdin are capped at 1 MiB.
  • verify confirms that the saved file reopens. It does not check visual fidelity or whether the edit is semantically correct; use reads and previews for that.
  • Previews use the BetterOffice layout engine and bundled fonts and are not pixel-identical to Office. Install @betteroffice/fonts-cjk for CJK glyphs.

Documentation