Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ All notable changes to ExcelMcp will be documented in this file.

## [Unreleased]

### Added
- **IRM/AIP-protected file support** in `file(action='open')`: Files protected with Azure Information Protection (AIP/IRM) are automatically detected via OLE2 Compound Document header signature (`D0 CF 11 E0 A1 B1 1A E1`). They are opened read-only with Excel forced visible so the Windows IRM credential prompt can appear — no extra parameters needed.
- **`isIrmProtected` field in `file(action='test')` response**: Reports whether a file uses the OLE2/IRM format before attempting to open it, enabling agents to pre-flight check and set expectations (IRM files are always read-only).

### Changed
- **MCP SDK upgraded** from `ModelContextProtocol` 0.8.0-preview.1 to 0.9.0-preview.2
- Updated `ImageContentBlock.Data` from `string` to `ReadOnlyMemory<byte>` (binary data API change)
- CLI daemon IPC migrated from custom protocol to StreamJsonRpc — improved reliability and error handling
- Enhanced daemon security with SID validation and `CurrentUserOnly` pipe access

Expand Down
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<PackageVersion Include="StreamJsonRpc" Version="2.24.84" />
<PackageVersion Include="System.Text.Json" Version="10.0.3" />
<!-- MCP SDK - Latest stable preview -->
<PackageVersion Include="ModelContextProtocol" Version="0.8.0-preview.1" />
<PackageVersion Include="ModelContextProtocol" Version="0.9.0-preview.2" />
<!-- Testing Framework -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="System.Net.Http" Version="4.3.5" />
Expand Down
4 changes: 2 additions & 2 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
## 📁 File Operations (6 operations)

- **List Sessions:** View all active Excel sessions
- **Open:** Open workbook and create session (returns session ID for all subsequent operations)
- **Open:** Open workbook and create session (returns session ID for all subsequent operations). IRM/AIP-protected files are automatically detected and opened read-only with Excel visible for credential authentication — no extra parameters needed.
- **Close:** Close session with optional save
- **Close Workbook:** Close workbook without closing Excel
- **Create Empty:** Create new .xlsx or .xlsm workbook
- **Test:** Verify workbook can be opened and is accessible
- **Test:** Verify workbook can be opened and is accessible. Returns `isIrmProtected` flag for IRM/AIP-protected files.

---

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
- 📄 **Worksheets** (2 tools, 16 ops) - Lifecycle, colors, visibility, cross-workbook moves
- 🔌 **Connections** (1 tool, 9 ops) - OLEDB/ODBC management and refresh
- 🏷️ **Named Ranges** (1 tool, 6 ops) - Parameters and configuration
- 📁 **Files** (1 tool, 6 ops) - Session management and workbook creation
- 📁 **Files** (1 tool, 6 ops) - Session management, workbook creation, IRM/AIP-protected file support
- 🧮 **Calculation Mode** (1 tool, 3 ops) - Get/set calculation mode and trigger recalculation
- 🎚️ **Slicers** (1 tool, 8 ops) - Interactive filtering for PivotTables and Tables
- 🎨 **Conditional Formatting** (1 tool, 2 ops) - Rules and clearing
Expand Down
2 changes: 1 addition & 1 deletion src/ExcelMcp.CLI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ ExcelMcp.CLI provides **225 operations** across 17 command categories:

| Category | Operations | Examples |
|----------|-----------|----------|
| **File & Session** | 6 | `session create`, `session open`, `session close`, `session list` |
| **File & Session** | 6 | `session create`, `session open` (IRM/AIP auto-detected), `session close`, `session list` |
| **Worksheets** | 16 | `sheet list`, `sheet create`, `sheet rename`, `sheet copy`, `sheet move`, `sheet copy-to-file` |
| **Power Query** | 10 | `powerquery list`, `powerquery create`, `powerquery refresh`, `powerquery update` |
| **Ranges** | 42 | `range get-values`, `range set-values`, `range copy`, `range find`, `range merge-cells` |
Expand Down
38 changes: 37 additions & 1 deletion src/ExcelMcp.ComInterop/FileAccessValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,46 @@ namespace Sbroenne.ExcelMcp.ComInterop;

/// <summary>
/// Utility class for validating file access and locking status.
/// Provides OS-level file lock detection before Excel COM operations.
/// Provides OS-level file lock detection and IRM/AIP-encryption detection before Excel COM operations.
/// </summary>
public static class FileAccessValidator
{
// OLE2 Compound Document Format signature.
// IRM/AIP-protected Excel files are stored as OLE2 containers with an EncryptedPackage
// stream instead of the standard ZIP-based Office Open XML format.
private static ReadOnlySpan<byte> Ole2Signature => [0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1];

/// <summary>
/// Detects if the file is IRM/AIP-protected by checking for the OLE2 compound document
/// signature. IRM-protected files must be opened as read-only with Excel visible so the
/// user can authenticate through the Information Rights Management credential prompt.
/// </summary>
/// <param name="filePath">The file path to inspect.</param>
/// <returns>
/// <c>true</c> if the file has the OLE2 Compound Document header, indicating IRM/AIP
/// encryption; <c>false</c> for standard ZIP-based .xlsx/.xlsm files or if the file
/// cannot be read.
/// </returns>
public static bool IsIrmProtected(string filePath)
{
if (!File.Exists(filePath))
return false;
try
{
Span<byte> header = stackalloc byte[8];
using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
int read = fs.Read(header);
if (read < 8)
return false;
return header.SequenceEqual(Ole2Signature);
}
catch
{
// Cannot read → treat as not IRM so normal error handling takes over
return false;
}
}

/// <summary>
/// Validates that a file is not locked by attempting to open it with exclusive access.
/// Throws InvalidOperationException if file is locked or inaccessible.
Expand Down
24 changes: 21 additions & 3 deletions src/ExcelMcp.ComInterop/Session/ExcelBatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,31 @@ private ExcelBatch(string[] workbookPaths, ILogger<ExcelBatch>? logger, bool sho
else
{
// OPEN EXISTING FILE: Validate and open
// CRITICAL: Check if file is locked at OS level BEFORE attempting Excel COM open
FileAccessValidator.ValidateFileNotLocked(path);
bool isIrm = FileAccessValidator.IsIrmProtected(normalizedPath);

if (isIrm)
{
// IRM/AIP-protected files are OLE2 containers that cannot be opened
// exclusively. Excel must be visible so the user can authenticate
// through the IRM credential prompt.
tempExcel.Visible = true;
_logger.LogDebug(
"IRM-protected file detected: {FileName}. Forcing Excel visible and opening read-only.",
Path.GetFileName(normalizedPath));
}
else
{
// CRITICAL: Check if file is locked at OS level BEFORE attempting Excel COM open
FileAccessValidator.ValidateFileNotLocked(path);
}

// Open workbook with Excel COM
try
{
wb = (Excel.Workbook)tempExcel.Workbooks.Open(path);
wb = isIrm
// ReadOnly=true prevents "exclusive access required" errors on IRM-encrypted files
? (Excel.Workbook)tempExcel.Workbooks.Open(normalizedPath, ReadOnly: true)
: (Excel.Workbook)tempExcel.Workbooks.Open(path);
}
catch (COMException ex) when (ex.HResult == unchecked((int)0x800A03EC))
{
Expand Down
2 changes: 2 additions & 0 deletions src/ExcelMcp.Core/Commands/FileCommands.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Sbroenne.ExcelMcp.ComInterop;
using Sbroenne.ExcelMcp.Core.Models;

namespace Sbroenne.ExcelMcp.Core.Commands;
Expand Down Expand Up @@ -38,6 +39,7 @@ public FileValidationInfo Test(string filePath)
Extension = extension,
LastModified = lastModified,
IsValid = exists && isValidExtension,
IsIrmProtected = exists && FileAccessValidator.IsIrmProtected(filePath),
Message = message
};
}
Expand Down
8 changes: 8 additions & 0 deletions src/ExcelMcp.Core/Models/ResultTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,14 @@ public class FileValidationInfo
/// </summary>
public bool IsValid { get; set; }

/// <summary>
/// Whether the file is IRM/AIP-protected (OLE2 compound document format).
/// IRM-protected files are opened as read-only with Excel made visible so the user
/// can authenticate through the Information Rights Management credential prompt.
/// Use <c>show=true</c> when opening—this is set automatically by ExcelBatch when IRM is detected.
/// </summary>
public bool IsIrmProtected { get; set; }

/// <summary>
/// Optional message describing validation outcome (missing file, invalid extension, etc.)
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/ExcelMcp.McpServer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ dotnet tool install --global Sbroenne.ExcelMcp.CLI
- 📄 **Worksheets** (2 tools, 16 ops) - Lifecycle, colors, visibility, cross-workbook moves
- 🔌 **Connections** (1 tool, 9 ops) - OLEDB/ODBC management and refresh
- 🏷️ **Named Ranges** (1 tool, 6 ops) - Parameters and configuration
- 📁 **Files** (1 tool, 6 ops) - Session management and workbook creation
- 📁 **Files** (1 tool, 6 ops) - Session management, workbook creation, IRM/AIP-protected file support
- 🧮 **Calculation Mode** (1 tool, 3 ops) - Get/set calculation mode and trigger recalculation
- 🎚️ **Slicers** (1 tool, 8 ops) - Interactive filtering for PivotTables and Tables
- 🎨 **Conditional Formatting** (1 tool, 2 ops) - Rules and clearing
Expand Down
5 changes: 5 additions & 0 deletions src/ExcelMcp.McpServer/Tools/ExcelFileTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public static partial class ExcelFileTool
/// TIMEOUT: Each operation has a 5-min default timeout. Use timeoutSeconds to customize
/// for long-running operations (data refresh, large queries). Operations timing out
/// trigger aggressive cleanup and may leave Excel in inconsistent state.
///
/// IRM/AIP FILES: Files protected with Azure Information Protection are detected automatically.
/// They are opened as read-only with Excel forced visible for credential authentication.
/// Use 'test' action first to check isIrmProtected before attempting to open.
/// </summary>
/// <param name="action">The file operation to perform</param>
/// <param name="path">Full Windows path to Excel file (.xlsx or .xlsm). ASK USER for the path - do not guess or use placeholder usernames. Required for: open, create, test</param>
Expand Down Expand Up @@ -349,6 +353,7 @@ private static string TestFileAsync(string path)
extension = info.Extension,
size = info.Size,
lastModified = info.LastModified,
isIrmProtected = info.IsIrmProtected,
message = info.Message,
isError = info.IsValid ? (bool?)null : true
}, ExcelToolsBase.JsonOptions);
Expand Down
6 changes: 1 addition & 5 deletions src/ExcelMcp.McpServer/Tools/ExcelScreenshotTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,7 @@ public static CallToolResult ExcelScreenshot(
{
Content =
[
new ImageContentBlock
{
Data = result.ImageBase64,
MimeType = result.MimeType
},
ImageContentBlock.FromBytes(Convert.FromBase64String(result.ImageBase64), result.MimeType),
new TextContentBlock
{
Text = metadata
Expand Down
2 changes: 1 addition & 1 deletion vscode-extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ The Excel MCP Server (excel-mcp) provides **25 specialized tools with 225 operat
- 📄 **Worksheets** (2 tools, 16 ops) - Lifecycle, colors, visibility, cross-workbook moves
- 🔌 **Connections** (1 tool, 9 ops) - OLEDB/ODBC management and refresh
- 🏷️ **Named Ranges** (1 tool, 6 ops) - Parameters and configuration
- 📁 **Files** (1 tool, 6 ops) - Session management and workbook creation
- 📁 **Files** (1 tool, 6 ops) - Session management, workbook creation, IRM/AIP-protected file support
- 🎚️ **Slicers** (1 tool, 8 ops) - Interactive filtering for PivotTables and Tables
- 🎨 **Conditional Formatting** (1 tool, 2 ops) - Rules and clearing
- 📸 **Screenshot** (1 tool, 2 ops) - Capture ranges/sheets as PNG for visual verification
Expand Down