Release notes and version history for Excel MCP Server
All notable changes to ExcelMcp will be documented in this file.
This changelog covers all components:
MCP tool cancellation could leave the in-process server wedged until Excel was killed manually: The MCP ServiceBridge created timeout and cancellation tokens but never applied them to the in-process service call, and tool methods did not flow request cancellation into the bridge. When VS Code cancelled a long-running tool call, the Excel COM work could continue on a blocked batch thread while the poisoned session remained in the server, making subsequent requests appear hung. Fixed by running bridge dispatch on a separate task, force-closing the affected session or resetting the service on timeout/cancellation, and propagating request cancellation from MCP tool methods through the shared tool base into the bridge.
Remaining synchronous Power Query and connection load paths could still deadlock despite the earlier refresh fix: powerquery evaluate, powerquery load-to, powerquery create load destinations, and connection worksheet/data refreshes still wrapped QueryTable.Refresh(false) or connection.Refresh() in EnterLongOperation(). That reused the same callback-rejection pattern that previously deadlocked normal Power Query refresh: Excel and MashupHost could not deliver the inbound COM callbacks needed to complete the synchronous load. Fixed by removing EnterLongOperation() from those paths too and switching them to the same OleMessageFilter.SetPendingCancellationToken(...) pattern used by the repaired Power Query refresh implementation.
powerquery refresh could hang indefinitely (permanent COM deadlock): EnterLongOperation was called before QueryTable.Refresh(false) and connection.Refresh() in the PowerQuery refresh path. EnterLongOperation sets _isInLongOperation=true, causing HandleInComingCall to return SERVERCALL_RETRYLATER for ALL inbound COM calls — including essential MashupHost callbacks Excel needs to complete the synchronous refresh. This created a permanent mutual deadlock (observed: 30-minute hang in production on a worksheet-loaded query). Fixed by removing EnterLongOperation from both refresh paths and registering a CancellationToken with OleMessageFilter so MessagePending returns PENDINGMSG_CANCELCALL when the token fires, enabling clean STA thread exit. Trade-off: Elevated CPU (~88%) during refresh is accepted as preferable to a permanent hang. The CPU spin regression tests (PowerQueryRefreshCpuSpinTests) are now intentionally expected to fail — they are excluded from CI via RunType=OnDemand and updated to document this known trade-off.
Structural COM stability improvements: Addressed root cause of intermittent operation hangs and orphan Excel processes. ExcelBatch.Execute() now automatically suppresses ScreenUpdating via a new ExcelWriteGuard, reducing COM callbacks and improving bulk operation performance. Unified multi-workbook shutdown to use the resilient ExcelShutdownService (was bare COM calls without retry). Added retry logic to workbook Save (for file locks) and Close (for COM busy errors). Excel process ID capture now retries 3 times with 500ms delay to prevent force-kill from being permanently disabled under load. Added safety-net ProcessExit handler that kills tracked Excel processes on unexpected .NET process termination.
Operation hangs with error handling improvements: Hardened the service dispatch layer so that cleanup failures during error handling no longer propagate secondary exceptions. Dead Excel sessions are now automatically detected and cleaned up in all exception paths. Power Query Evaluate Refresh() is now wrapped with EnterLongOperation to prevent CPU spin during M code evaluation. Added cancellation checks to COM collection loops for large workbooks.
PivotTable test suite no longer hangs: Fixed a test fixture deadlock caused by using both IClassFixture and a collection fixture on the same test class, which created concurrent Excel sessions that deadlocked. Consolidated to a single shared fixture — all 102 PivotTable tests now pass reliably.
range set-formulas and range get-formulas injected @ implicit intersection operator inside Excel Tables: The legacy Range.Formula COM property automatically prepends @ to formulas inside structured tables, causing #FIELD! errors with custom functions that return entity cards (e.g., Office Add-in rich data types). Switched to Range.Formula2 (Excel 365+) which respects dynamic array semantics and does not inject @.
Connection refresh and PowerQuery refresh / refresh-all could hang or miss cancellation on async data sources: WorkbookConnection.Refresh() returns immediately when the provider runs asynchronously, leaving the STA thread without a way to detect completion or honour the operation timeout. Both Connection and PowerQuery refresh now set the sub-connection’s BackgroundQuery = true, call Refresh(), then poll .Refreshing in a loop that responds to cancellation and calls .CancelRefresh() when the timeout fires. powerquery refresh-all was also updated to use the same robust RefreshConnectionByQueryName path (which includes QueryTable.Refresh(false) for worksheet queries) instead of a bare connection.Refresh().
CLI and MCP Server version always reported as 1.0.0 (#523): The update check and About dialog always showed version 1.0.0 instead of the actual installed version. Fixed by removing hardcoded version properties from project files so they inherit from the central version configuration.
table append JsonElement COM marshalling (#519): Row values containing booleans or strings were passed as raw System.Text.Json.JsonElement to cell.Value2, which COM interop cannot marshal to a Variant. Fixed by calling RangeHelpers.ConvertToCellValue() (the same fix already present in range set-values) to unwrap JsonElement to native types before assignment.
--values/--rows inline JSON: PowerShell quote-stripping + stdin sentinel (#521): Windows CreateProcess strips inner double-quotes when PowerShell passes arguments to native executables, so --values '[["ACD Full Term",0.26]]' arrives as [[ACD Full Term,0.26]] (invalid JSON). The generated DeserializeNestedCollection<T> now: (1) emits a clear error message that mentions --values-file and --values - as workarounds, and (2) supports a stdin sentinel — passing --values - (or --rows -) reads the JSON from Console.In, avoiding shell quoting entirely.
Table add-to-data-model bracket column names block DAX formulas: Excel table columns with literal bracket characters in their names (e.g., from OLEDB import sources) cannot be referenced in DAX formulas after being added to the Data Model. Added new stripBracketColumnNames parameter (default: false). When false, bracket column names are reported in bracketColumnsFound so users are aware of the issue. When true, the source table column headers are renamed (brackets removed) before adding to the Data Model, enabling full DAX access. The add-to-data-model result now includes bracketColumnsFound and bracketColumnsRenamed fields.
PowerQuery load-to data-model silently succeeded without loading data: powerquery load-to with data-model destination returned success: true but the table never appeared in the Power Pivot Data Model. The connection was registered via Connections.Add2() but connection.Refresh() was never called, so data was not actually loaded. Fixed by calling connection.Refresh() after creating the connection, consistent with how load-to worksheet works.
chartconfig set-data-labels threw raw COMException on Line charts with bar-only position: Setting labelPosition to InsideEnd, InsideBase, or OutsideEnd on a Line chart threw a raw COM exception with no user-friendly explanation. These positions are only valid for bar, column, and area chart types. Fixed by catching the COMException and throwing an InvalidOperationException with a descriptive message explaining which chart types support each position, consistent with how ShowPercentage handles unsupported chart types.
rangeformat format-range parameter documentation listed wrong valid values for borderStyle: The borderStyle parameter help incorrectly listed thin, medium, thick, dashed, and dotted as valid values — those are borderWeight values. The valid borderStyle values are continuous, dash, dot, dashdot, dashdotdot, double, slantdashdot, and none. Documentation corrected.
rangeformat format-range rejected middle as a vertical alignment value: The verticalAlignment parameter only accepted center but not the common alias middle. Both now accepted and produce identical center-vertical alignment.
screenshot CLI --output flag documentation clarified: The --output <path> flag saves the screenshot directly to a PNG or JPEG file instead of printing base64 JSON to stdout. This was already functional but was documented as “For CLI: saved to file” without explaining that --output is required to save to a file.
office.dll not found when opening workbooks with connections/data model (#487 follow-up): The AssemblyResolve handler only searched AppContext.BaseDirectory for office.dll. In NuGet-installed tool deployments, office.dll is never copied there (it is only present in local dev builds via Directory.Build.targets). Opening workbooks with external connections, Power Query, or a Data Model triggered code paths that caused the CLR to load Microsoft.Office.Interop.Excel.dll, which in turn requested office.dll v16. The handler returned null → FileNotFoundException. Fixed by adding fallback search order: (1) AppContext.BaseDirectory, (2) .NET Framework GAC v16, (3) GAC v15 (accepted by CLR as substitute), (4) Office 365 click-to-run installation directories. Directory.Build.targets also updated to prefer v16 GAC when available.
Microsoft.Office.Interop.Excel types for improved reliability and compile-time error detection. Power Query APIs (Workbook.Queries) and VBA project access remain as dynamic calls where PIA coverage is unavailable.All Excel sessions crashed with FileNotFoundException for office.dll (#487): After PIA migration, ExcelBatch STA thread declared tempExcel as typed Excel.Application. Casting a typed COM interop object to (dynamic) retains PIA type metadata; the DLR then resolved MsoAutomationSecurity from office.dll (Microsoft.Office.Core v16.0.0.0) at runtime, which is not bundled with the deployed .NET tool. Every session (create and open) crashed before opening any workbook. Fixed by casting to (object) first before (dynamic) to force pure IDispatch binding. Also removed a broken <Reference> to office.dll with a wrong v15.0.0.0 hint path (runtime required v16.0.0.0).
OleMessageFilter.MessagePending was returning 2 (PENDINGMSG_WAITNOPROCESS) instead of 1 (PENDINGMSG_WAITDEFPROCESS). When Excel fires a re-entrant callback (e.g. Calculate/SheetChange event) during a FormatConditions.Add() call, WAITNOPROCESS blocked COM from delivering the callback — Excel waited for the callback while the STA thread waited for Excel, causing a permanent deadlock. Any operation that triggers Excel’s internal event loop (conditional formatting on formula cells, PivotTable refresh, Power Query refresh) was affected. Fixed by returning 1 so COM delivers pending inbound calls during the outgoing IDispatch.Invoke.IDispatch.Invoke, WithSessionAsync had no catch (OperationCanceledException) handler — the session remained alive with a permanently blocked STA thread, causing all subsequent operations to hang. Fixed by adding catch (OperationCanceledException) that force-closes the session (same pattern as the existing TimeoutException handler).Slow Fail on Successive Calls After Timeout/Cancellation: After a timeout or cancellation, Execute<T> would queue new work on a permanently stuck STA thread, forcing each subsequent caller to wait for its own full timeout before failing. Fixed by adding a fail-fast pre-check: if _operationTimedOut is set, throw TimeoutException immediately.
Task.Run(() => workbook.Save()) in ExcelShutdownService — this marshalled the COM call from the STA thread to an MTA thread-pool thread, which is incorrect and fragile in .NET 8+. Save is now called directly on the STA thread, which is always the case inside ExcelBatch.Execute().ExcelBatch. When the Hwnd path fails, force-kill is now disabled with a warning rather than risking killing an unrelated Excel workbook the user has open.Thread.Sleep in Dispose (#482): Removed 100 ms Thread.Sleep from ExcelBatch.Dispose(). The preceding _shutdownCts.Cancel() call immediately wakes the STA thread from WaitToReadAsync, making the sleep redundant and adding unnecessary latency.ExcelMcpService top-level catch blocks now return "{ExType}: {ex.Message}" instead of just ex.Message, making unexpected failures distinguishable without a full stack trace.WaitForSingleObject; ExcelMcpService catches TimeoutException to prevent unhandled exceptionsIConfiguration reload-on-change in MCP Server to prevent 85%+ CPU usage from FileSystemWatcher pollingProcess object not being disposed in ExcelBatch.ForceKillExcelProcess()ServiceInfoExtractor that could lose type information across partial interfacestype → trendlineType in IChartConfigCommands to avoid COM parameter ambiguitySetStyle error message to show valid range when styleId is out of boundsInvalidOperationException in chart appearance commandsFile.Copy() instead of creating individual files via COM, eliminating ~74 redundant Excel sessionsquality parameter on screenshot tool (High/Medium/Low). Default is Medium (JPEG at 75% scale, ~4–8x smaller than original PNG). Use High (PNG, full scale) when fine text needs careful inspection, Low (JPEG at 50% scale) for layout overviews.window tool with 9 operations to control Excel window visibility, position, state, and status bar — enabling “Agent Mode” where users watch AI work in Excel
show / hide — Toggle Excel visibility (syncs with session metadata)bring-to-front — Bring Excel to foregroundget-info — Query window state (visibility, position, size, foreground status)set-state — Set normal / minimized / maximizedset-position — Set window left, top, width, heightarrange — Preset layouts: left-half, right-half, top-half, bottom-half, center, full-screenset-status-bar — Display live operation status text in Excel’s status barclear-status-bar — Restore default status bar text--output flag for all commands: Save command output directly to a file. Screenshot commands automatically save decoded PNG images instead of base64 JSONexcelcli batch command executes multiple CLI commands from a JSON file in a single process launch
session.open/session.create, auto-clear on session.close--stop-on-error flag to halt on first failure (default: continue all)--help crash (#463): Fixed Spectre.Console markup crash when parameter descriptions contain [/] characters (e.g., [A1 notation])mcpTool ?? "unknown" fallback; added HasMcpToolAttribute to correctly filter MCP-only toolsconditionalformat.md and slicer.md reference filesCO_E_SERVER_EXEC_FAILURE, RPC_E_CALL_FAILED) during Excel process startup under resource constraintsSbroenne.ExcelMcp.CLI alongside MCP Server
net10.0 → net10.0-windows) and formatting errors in build workflowSee BREAKING-CHANGES.md for complete migration guide.
LLMs pick up these changes automatically via tools/list (MCP) and --help (CLI).
excel_ prefix from all 23 MCP tool names (e.g., excel_range → range, excel_file → file). Titles also shortened (e.g., "Chart Operations"). VS Code extension server name → excel-mcp.calculation_mode tool/CLI command (automatic, manual, semi-automatic modes; workbook/sheet/range scopes)npx add-mcp as primary installation method in docsexcel_ prefix from prompt nameschatSkills contribution pointnet10.0-windowsDockerfile, glama.json, .dockerignore, docs)RefreshTable() called after each field operation triggered synchronous Analysis Services queriespivottable(refresh) explicitly to update visual display after configuring fieldssuccess, errorMessage, filePath)[JsonPropertyName] attributes from model filesconnection set-properties: Added description, backgroundQuery, savePassword, refreshPeriodpowerquery create/load-to: Added targetSheet, targetCellAddresschart create-* and move: Added left, top, width, heighttable append: Fixed to parse CSV into proper rows formatvba run: Added timeoutSecondscheck-cli-settings-usage.ps1) to prevent future occurrencesSessionManager never checked if Excel process was alive, leaving dead sessions in dictionaryGetSession(), GetActiveSessions(), and IsSessionAlive() now check process health and auto-cleanupExcelBatch.Execute() validates Excel is alive before queueing operationsSessionManager)SetFormulas, SetValues, Table.Append, NamedRange.Write=INDEX(KPIs[Total_ACR],1) now work without “The operation was canceled” errorrefresh action returned success: true even when Power Query had formula errors
Connection.Refresh() silently swallows errors for worksheet queries (InModel=false)QueryTable.Refresh(false) for worksheet queries which properly throws errorsConnection.Refresh() which does throw errors"[Expression.Error] The name 'Source' wasn't recognized..."table create --range A1 created single-cell table
ListObjects.Add() doesn’t auto-expand from a single cellRange.CurrentRegion when single cell provided, capturing all contiguous dataevaluate action to execute M code directly and return results
excelcli powerquery evaluate --file data.xlsx --mcode "let Source = #table({\"Name\",...})"mCodeFile parameter on powerquery tool for create, update, evaluate actionsmCode and mCodeFile providedvbaCodeFile parameter on vba tool for create-module, update-module actionsvbaCode and vbaCodeFile provided--session parameter (was positional in some commands)--help descriptions on all commands synced with MCP tool descriptions--file parameters support both new file creation and existing filesexcelcli list-actions command to discover all available operations-q/--quiet flag suppresses banner for agent-friendly JSON-only output
Version Check: excelcli version --check queries NuGet to show if update available
--save flag for atomic save-and-close workflow
check-cli-action-coverage.ps1 script
timeoutSeconds parameter on file(open) and file(create) actionsTimeoutExceptioncreate-and-open to simpler create action
unload action removes data from all load destinations
skills/excel-cli/ - CLI-specific skill with commands referenceskills/excel-mcp/ - MCP Server skill with tools referenceskills/shared/ - Shared workflows, anti-patterns, behavioral rules--timeout <seconds> (was --timeout-seconds)--session parameterslicer tool for interactive filtering
execute-dmv action on datamodel toolevaluate action on datamodel tool for ad-hoc DAX queriescreate-from-dax, update-dax, get-dax actionsrename action for Power Query queriesrename-table action for Data Model tablesisError signaling for tool execution failuresRefreshTable() callscopy-to-file and move-to-file actionsCreateFromPivotTable now works with OLAP/Data Model PivotTablesshowExcel: true to watch AI changes liveget-data action returns table rowsdelete actionmove actionset-load-to-data-model failuresexcel_querytable toolcreate action