Skip to content

MCP workflow ​

formulon-mcp keeps an opened workbook in a named in-memory session. The usual workflow is:

01formulon_open_workbookReturns session.id;recalculates once02Inspect and readformulon_inspect_workbook, get_range03Mutateset_cells recalculates bydefault04formulon_recalc_sessionRefreshes formula resultsexplicitly05Previewformulon_preview_rangerenders an image06formulon_save_sessionWrites the file; does notrecalculate07formulon_close_workbookEnds the session01formulon_open_workbookReturns session.id; recalculates once02Inspect and readformulon_inspect_workbook, get_range03Mutateset_cells recalculates by default04formulon_recalc_sessionRefreshes formula results explicitly05Previewformulon_preview_range renders an image06formulon_save_sessionWrites the file; does not recalculate07formulon_close_workbookEnds the session
Saving does not recalculate; call formulon_recalc_session first when the output needs every formula result refreshed.

Open once, use the returned session.id for subsequent calls, and save to a new path with an explicit outputPath. A session is isolated from every other session. Its workbook, formula cache, dirty state, and dependency graph are not shared.

Open and inspect ​

Open an existing workbook with a stable id when the agent will make several calls:

json
{
  "path": "input.xlsx",
  "sessionId": "monthly-edit"
}

formulon_open_workbook returns { "session": ... }. The session record contains:

FieldMeaning
idThe id used in later calls
sourcePathThe input path, when a file was opened
outputPathThe most recent save destination, when one exists
createdAt, updatedAtISO timestamps maintained by the server
dirtyThe in-memory workbook may differ from its last successful lossless save
loadLossesNon-zero reader diagnostics, when part of the input could not be decoded

When tools recalculate ​

Some reads and writes recalculate automatically. A save serializes the current workbook state; it does not run recalculation.

ToolDefault recalcEffect
formulon_set_cells, formulon_set_rangetrueWrites the values, then recalculates. Set false to defer calculation.
formulon_get_celltrueRecalculates before returning one cell. Set false to read the current cache.
formulon_get_rangefalseReads the current cached values. Set true to recalculate first.
formulon_preview_rangefalseRenders current values. Set true to recalculate before rendering.
formulon_audit_formulasfalseAudits the current formula and cached-value state. Set true to recalculate first.
formulon_recalc_session—Recalculates the session explicitly.
formulon_save_session—Saves current state without recalculating.

dirty means that the in-memory workbook may differ from the last successful lossless save. A write or recalculation can mark the session dirty. This flag does not say whether every formula cache is current: call formulon_recalc_session before saving when the output needs all formula results refreshed. A partial recalculation or a save alone can leave unrelated cached values stale.

Opening a file recalculates it once. This makes the first formula read use calculated values rather than a stale cached blank. A newly created session has the first-sheet name selected by its Excel profile and no sourcePath. Workflows that refer to Sheet1 should pass profile: "win-365-en_US" explicitly; otherwise use the name returned by inspection for the selected profile.

Inspect structure before addressing cells:

json
{
  "sessionId": "monthly-edit",
  "includeCells": false,
  "maxCellsPerSheet": 200
}

formulon_inspect_session returns session and a workbook summary containing sheets, definedNames, and native tables. Set includeCells to true to include sparse cell entries; maxCellsPerSheet defaults to 200 and is capped at 10,000. Its cellCount and cells use the engine's written-coordinate inventory, including formatted cells, explicit blanks, and spill phantoms. formulon_get_range is a value/formula read and omits formula-free blanks, including styled blanks.

Use formulon_inspect_layout when the task depends on used ranges, merges, dimensions, views, protection, formulas, or styles. Its default includes cells and allows 10,000 cells per selected sheet. Set includeStyles: true when the agent needs the resolved style records. formulon_detect_regions and formulon_analyze_workbook provide deterministic hints for unfamiliar sheets; they do not replace reading the source data. formulon_list_sessions shows the sessions already open, and formulon_session_metadata returns the registered function names or the workbook's external links.

Excel profile ​

A profile governs number, date, and boolean text, TEXT format codes, collation (comparison, lookups, sorting), CHAR / CODE code pages, and the double-byte functions (*B, ASC, DBCS). Profile ids are {mac|win}-365-<locale>, where the locale is one of en_US, ja_JP, de_DE, fr_FR, es_ES, es_MX, pt_BR, it_IT, nl_NL, ru_RU, zh_CN, zh_TW, ko_KR, or th_TH. The mac-* profiles are measured from Mac Excel; the win-* profiles are estimated from them plus Windows host rules, except text collation, which uses a locale-aware collator measured on Windows Excel 365 (ß equals ss except in an exact, strength 2 comparison, as on Mac). The default is win-365-en_US.

formulon_open_workbook, formulon_inspect_workbook, and formulon_update_workbook accept an optional profile. For a new workbook it creates the workbook as Excel creates it in that locale: body font, Normal style, sheet names, and margins. For a loaded file it sets the evaluation profile before the open-time recalculation. When profile is omitted, the server environment variable FORMULON_MCP_PROFILE applies, then win-365-en_US. An unknown id is an error; when it comes from the environment variable, the message says so.

json
{
  "sessionId": "ja-book",
  "profile": "mac-365-ja_JP"
}

The profile is a runtime setting and is not stored in the .xlsx. For formulon_open_workbook, formulon_inspect_workbook, and formulon_update_workbook, the requested profile wins, followed by FORMULON_MCP_PROFILE, then win-365-en_US. A path-mode formulon_get_cell read calls loadWorkbook directly and does not apply FORMULON_MCP_PROFILE; use a session opened with the required profile for locale-dependent reads.

To switch within a session, call formulon_workbook_call with setExcelProfileId and args of ["mac-365-ja_JP"], then formulon_recalc_session. This changes evaluation only, not fonts or sheet names. excelProfileId reads the current value.

Read values and formulas ​

Use a bounded A1 range for a table or a single cell for a spot check:

json
{
  "sessionId": "monthly-edit",
  "range": "Summary!A1:H24",
  "includeFormulas": true,
  "recalc": false
}

formulon_get_range returns a sparse list. The engine enumerates populated cells in the requested rectangle, including formulas and spill cells, and omits styled blanks. The result is capped by maxCells (default 10,000, maximum 50,000), with truncated only when additional cells exist. The rectangle has no scan-area cap. Each returned cell has its A1 address, zero-based coordinates, and a {kind, ...} value envelope. includeFormulas adds a formula string, empty for constants. Numeric cells with a date, currency, or percent format can also carry numberFormat, formatKind, and a decoded formatted value. Non-General numeric formats include engine display text when available; textStatus identifies overflow or invalidFormat.

formulon_get_cell accepts exactly one of sessionId or path. Pass a1, or pass row and col; sheet defaults to index 0. Its default recalc: true recalculates a session before reading and therefore marks that session dirty. Set it to false for a read that must leave the cached calculation state untouched. A path read is one-shot, does not create a session, and loads without applying FORMULON_MCP_PROFILE; use sessionId for a locale-dependent read.

formulon_eval_formula evaluates one formula without writing a cell. In session mode it resolves workbook references and defined names, and uses row and col (both default 0) as the anchor for relative references and ROW() / COLUMN(); it uses that session's profile. In global mode it evaluates against the engine's default profile and cannot use FORMULON_MCP_PROFILE, so use a profiled session for locale-dependent formulas. A leading = is optional.

formulon_trace follows one cell's references. precedents lists the cells it reads, dependents lists the cells that read it, and spillInfo reports the range a dynamic-array formula spills into. Results are A1 references with sheet names; depth (default 1, maximum 32) follows the chain further.

Mutate cells and structure ​

formulon_set_cells is useful for a small set of unrelated addresses:

json
{
  "sessionId": "monthly-edit",
  "mutations": [
    { "type": "text", "a1": "Summary!B2", "value": "2026-10" },
    { "type": "number", "a1": "Summary!B3", "value": 42 },
    { "type": "formula", "a1": "Summary!B4", "formula": "=B3*1.1" }
  ],
  "recalc": true
}

Each mutation is number, bool, text, blank, or formula. Address a cell with a1, or with sheet plus zero-based row and col. A batch accepts 1 to 10,000 mutations and defaults to recalc: true. The response contains session, an applied entry for each successful write, and errorCells for written formulas whose result is an Excel error.

For a rectangular table, formulon_set_range is more compact:

json
{
  "sessionId": "monthly-edit",
  "start": "Summary!B8",
  "values": [
    ["Item", "Qty", "Amount"],
    ["Design", 2, { "f": "=C9*120000" }],
    ["Build", 1, { "f": "=C10*98000" }]
  ],
  "recalc": true
}

The row-major values array accepts numbers, booleans, strings, {"f":"=..."} formulas, {"blank":true} clears, and null skips the cell. start is an A1 anchor; sheet is the fallback when the anchor is unqualified. The response reports the start and end addresses, cellsWritten, and errorCells.

Cell mutations validate every address before the first write. If the native engine fails after a write has started, earlier writes remain in the session and the session stays dirty. Review applied and errorCells on a successful response; after an MCP tool error, inspect the affected range before retrying.

Use the structure tools for sheet order, defined names, rows, columns, and sheet view. formulon_sheet_operation supports add, remove, rename, and move; the corresponding fields are name, index, newName, fromIndex, and toIndex. formulon_set_defined_name uses workbook scope when sheet is omitted, sheet-local scope otherwise, and removes a name when formula is empty. A stored name's formula reads back without a leading =. comment and hidden set the note and the Name Manager visibility of the name; replacing a formula keeps both unless they are given. _xlnm.Print_Area and _xlnm.Print_Titles must be sheet-local for Excel to apply them. formulon_edit_structure inserts or deletes rows or columns and lets the engine rewrite affected references. formulon_set_sheet_view accepts zoom from 10 to 400, freeze counts, split positions, the scroll origin, formula and outline-symbol display, the active sheet, and either two-state hidden or three-state visibility (visible, hidden, veryHidden). When changing frozen panes, pass both freezeRows and freezeCols: an omitted count becomes 0 instead of keeping its previous value. The engine refuses to hide the last visible sheet. See Layout and previews for each field.

Comments, links, validation, and merges ​

Five tools edit per-sheet objects. Each takes sessionId, an optional sheet (default: the first sheet), and an operation. Unlike the cell tools, they address cells with zero-based numbers rather than A1 references.

ToolOperationsAddressing
formulon_comment_operationlist, get, set, remove, visibilityrow, col; set takes author and text; visibility takes visible (true keeps the comment open on the sheet)
formulon_hyperlink_operationlist, add, remove, removeAt, clearrow, col, optional lastRow / lastCol; target is a URL, or leave it empty and set location (such as Sheet2!A1) for an in-workbook link
formulon_validation_operationlist, add, removeAt, clearvalidation.ranges as {firstRow, firstCol, lastRow, lastCol}
formulon_conditional_format_operationlist, add, removeAt, clear, evaluaterule.sqref ranges; evaluate takes firstRow, firstCol, lastRow, lastCol
formulon_merge_operationlist, add, remove, removeAt, clearrange as {firstRow, firstCol, lastRow, lastCol}

removeAt takes the index from the matching list result. Each response is {session, method, result} with the engine result under result. Rule types, styles, evaluation, and worked examples for validation and conditional formatting are in Conditional formats and data validation. The exact field lists are in the tool reference.

Three more tools cover drawing shapes, sparklines, and What-If data tables. They take A1 cell references on the selected sheet rather than zero-based numbers, and return the object they changed. Shapes are shown in Layout and previews. A sparkline group plots one data range into one cell per sparkline, and a data table fills a result range by substituting values into input cells:

formulon_sparkline_operation ​

json
{
  "sessionId": "review",
  "operation": "add",
  "sparklines": [
    { "data": "B2:M2", "location": "N2" },
    { "data": "B3:M3", "location": "N3" }
  ],
  "type": "line",
  "high": true,
  "low": true,
  "colors": { "series": "#336699", "high": "#CC3333" }
}

The response is {session, sheet, group} with the group's index. Pass that index to update or remove; group indexes shift when one is removed.

formulon_data_table_operation ​

json
{ "sessionId": "review", "operation": "add", "range": "C3:E8", "rowInput": "H1", "colInput": "H2" }

This is a two-variable table over C3:E8: the formula goes in the corner cell B2, the values substituted into H1 in C2:E2, and the values substituted into H2 in B3:B8. With only one input, the formula sits in the row above the range (colInput) or the column to its left (rowInput). Result cells refuse writes until the table is removed. add recalculates, so the results can be read at once; recalc recomputes every data table, including under the automatic-except-tables calculation mode.

Recalculate deliberately ​

Open and mutation defaults already recalculate in common cases, but reads and inspection tools use different defaults. Recalculate explicitly when the next decision depends on current formula values:

json
{
  "sessionId": "monthly-edit"
}

This call is formulon_recalc_session. It returns { "session": ..., "status": ... } and marks the session dirty because formula caches are workbook state. The following calls can also recalculate when requested:

  • formulon_set_cells and formulon_set_range: recalc defaults to true.
  • formulon_replace_cells: recalc defaults to true.
  • formulon_get_cell: recalc defaults to true.
  • formulon_get_range, formulon_preview_range, and formulon_audit_formulas: recalc defaults to false.

An explicit recalc: false keeps a batch from doing repeated work. Apply several changes, call formulon_recalc_session once, then read the dependent range.

Search and replace ​

Search text values, formula text, or both:

json
{
  "sessionId": "monthly-edit",
  "query": "budget",
  "target": "both",
  "matchCase": false,
  "wholeCell": false,
  "regex": false,
  "maxResults": 1000
}

formulon_find_cells returns results, count, and truncated. Each result includes the sheet, A1 address, target kind (text or formula), and matched text. target defaults to both; target: "texts" also searches numeric and boolean constants as strings (for example 42, TRUE, and FALSE), but does not search formula results. maxResults defaults to 1,000 and is capped at 10,000. Omit sheet to search all sheets.

formulon_replace_cells adds replacement and uses the same matching options. It changes only string cells and formula text; numeric and boolean constants are not replaced. maxReplacements defaults to the supplied maxResults (1,000 if both are omitted). When specified, maxReplacements is the replacement cap; maxResults does not impose an additional cap. A replacement writes the original text or formula back through the engine, returns before, after, and a status for each replacement, and recalculates by default. A regex is compiled as global, with case sensitivity controlled by matchCase. wholeCell applies only when regex is false; for a full-cell regex match, anchor the expression with ^ and $.

Preview and save ​

Use a preview before saving when the change affects layout, styles, merges, or print settings:

Create review and out in the server's working directory before running this example. The save and preview tools do not create parent directories.

json
{
  "sessionId": "monthly-edit",
  "range": "Summary!A1:H24",
  "scale": 1,
  "showGridLines": false,
  "showPageBreaks": true,
  "recalc": false,
  "outputPath": "review/summary.png"
}

formulon_preview_range returns an image content item first and a JSON metadata item second. The metadata includes the selected sheet and range, logical and scaled dimensions, fonts, page count, page breaks, bounded cell geometry, and warnings. scale defaults to 1 and accepts 0.25 through 2. A preview covers at most 10,000 cells, each image edge is at most 4,096 pixels, the image is at most 8,000,000 pixels, and an SVG artifact is at most 4 MiB. outputPath may end in .png or .svg; a range that cuts across a merge is rejected.

Save to a new, explicit destination:

json
{
  "sessionId": "monthly-edit",
  "outputPath": "out/next-month.xlsx"
}

formulon_save_session returns the updated session, outputPath, byte count, selected format, and optional losses. .xlsx selects the XML writer; .xlsb selects the binary writer. The save operation does not recalculate automatically. If the writer drops or downgrades content, the session remains dirty. Always inspect losses before handing the output to a downstream process.

The server can fall back to a previous output path or the original source path when outputPath is omitted, but a workflow that must preserve the source should always pass the destination explicitly. A new session has no fallback and an omitted path fails with outputPath is required for a new workbook session.

Responses and errors ​

High-level success responses are JSON text in MCP content. The common session object carries the state needed for the next call. Cell values use these envelopes:

json
[
  { "kind": "number", "value": 1200 },
  { "kind": "text", "value": "Approved" },
  { "kind": "error", "errorCode": 1, "errorName": "#DIV/0!" }
]

Blank, boolean, array, reference, and lambda values retain their kind. Status-bearing engine results use { "status": { "ok": true, "status": 0, "message": "", "context": "" }, "value": ... } or the corresponding items field for list results. High-level tools add domain fields around that status.

When a tool fails, the server returns MCP isError: true with a text message. Typical causes are an unknown session, a missing required operation field, a sheet or range conflict, an unsupported extension, a failed engine status, or a path that cannot be read or written. The server validates before mutating where possible; native failures can still leave a partial mutation, so check the session's dirty flag and inspect the next range.

End-to-end template edit ​

Prompt an agent with a concrete artifact and destination:

Edit input.xlsx in session monthly-edit. Change Summary!B2 to 2026-10, keep the source untouched, recalculate Summary!B4, render Summary!A1:H24 to review/summary.png, save the workbook to out/next-month.xlsx, inspect the save losses, and close the session.

The important MCP calls are:

json
{
  "name": "formulon_open_workbook",
  "arguments": { "path": "input.xlsx", "sessionId": "monthly-edit" }
}
json
{
  "name": "formulon_set_cells",
  "arguments": {
    "sessionId": "monthly-edit",
    "mutations": [
      { "type": "text", "a1": "Summary!B2", "value": "2026-10" }
    ],
    "recalc": false
  }
}
json
{
  "name": "formulon_recalc_session",
  "arguments": { "sessionId": "monthly-edit" }
}
json
{
  "name": "formulon_get_range",
  "arguments": {
    "sessionId": "monthly-edit",
    "range": "Summary!B2:B4",
    "includeFormulas": true,
    "recalc": false
  }
}
json
{
  "name": "formulon_preview_range",
  "arguments": {
    "sessionId": "monthly-edit",
    "range": "Summary!A1:H24",
    "outputPath": "review/summary.png"
  }
}
json
{
  "name": "formulon_save_session",
  "arguments": {
    "sessionId": "monthly-edit",
    "outputPath": "out/next-month.xlsx"
  }
}
json
{
  "name": "formulon_close_workbook",
  "arguments": { "sessionId": "monthly-edit" }
}

If the agent needs only a single write and no follow-up read, formulon_update_workbook combines load or create, zero-based mutations, optional recalculation, and save. It still requires an explicit outputPath, accepts 1 to 10,000 mutations, and returns a summary plus save diagnostics. formulon_inspect_workbook is the corresponding one-shot read and defaults to no recalculation.