mirror of
https://github.com/sbroenne/mcp-server-excel.git
synced 2026-09-19 07:53:08 +08:00
df928e95f4
* feat: align CLI with MCP Server session API pattern MAJOR CHANGE: CLI now uses MCP-aligned session terminology - Old: batch-begin, batch-commit, batch-list - New: open, save, list (primary commands) - Legacy batch commands still work but map to new session commands IMPLEMENTATION: ✓ BatchCommands: Renamed Begin() → Open(), Commit() → Save() ✓ Program.cs: Updated routing for new command names ✓ Program.cs: Added backward compatibility for old batch commands ✓ Program.cs: Updated help text to show session lifecycle ✓ Program.cs: Banner now says 'MCP-aligned Session API' ✓ CommandHelper: Updated --batch-id → --session-id parameter detection ✓ Error messages: Updated terminology from 'batch' to 'session' ✓ All file operations: Updated comments and output messages BACKWARD COMPATIBILITY: ✓ Old batch-begin/batch-commit/batch-list commands still work ✓ --batch-id flag still accepted (mapped to --session-id internally) ✓ Both terminologies coexist during transition TESTING: ✓ CLI builds: 0 warnings, 0 errors ✓ MCP smoke test passed ✓ Help text displays new session commands and deprecation notice DOCUMENTATION: ✓ CLI README already updated in previous commit with session API examples ✓ Help output now clearly shows primary commands and deprecation path This aligns the CLI completely with the MCP Server's session-based API, making both interfaces consistent and simplifying user experience. * feat: split save/close commands - save keeps session open, close discards changes * refactor: remove backward compatibility layer - legacy GetBatch method and usage * fix: improve Range command error messages - use RangeHelpers specific errors - All Range operations now use RangeHelpers.ResolveRange() with specificError out parameter - Instead of cryptic '0x800A03EC' COM error, users now get helpful messages like: * 'Sheet \\'Validation_Summary\\' not found. Available sheets: Sheet1, Sheet2' * 'Named range \\'Sales\\' not found. Available named ranges: Parameters, Region' - Includes suggestions to use excel_worksheet or excel_namedrange tools - Affects: Get/Set Values/Formulas, Copy, Clear, Insert, Delete, Find, Replace, Sort, Hyperlinks, NumberFormat operations * fix: simplify COM error messages - remove LLM guidance Removed verbose step-by-step guidance from error messages. LLMs don't need to be told what to do, just what failed. Changed from: 'This usually means: (1) Sheet doesn't exist, (2) Range invalid, or (3) Session not open. Use excel_worksheet(action: list) to verify...' To: 'Cannot read range X on sheet Y: Insufficient memory to continue the execution of the program' Files changed: - RangeCommands.Values.cs: Simplified GetValuesAsync and SetValuesAsync COM error messages - RangeCommands.Formulas.cs: Simplified GetFormulasAsync and SetFormulasAsync COM error messages - ExcelToolsBase.cs: Simplified session validation error - just state facts, no instructions * docs: add error message style guidance - facts not instructions Added Best Practice #9 and Error Message Style section to mcp-server-guide. Key principle: Error messages should state facts (what failed, why), not prescribe solutions. LLMs are intelligent agents that determine next steps - they don't need step-by-step guidance. Example: ❌ Wrong: 'This usually means: (1) Sheet doesn't exist... Use excel_worksheet(list)...' ✅ Correct: 'Cannot read range X on sheet Y: Insufficient memory' This matches the simplifications in commit21d4c58. * fix: return JSON errors from OpenSessionAsync instead of throwing Changed OpenSessionAsync to catch exceptions from CreateSessionAsync and return JSON with success=false instead of letting them bubble up as unhandled exceptions. Fixes the generic 'An error occurred invoking excel_file' message shown when file was already open or other session creation errors occurred. Files changed: - ExcelFileTool.cs: Wrapped CreateSessionAsync in try-catch, return JSON errors * refactor: Replace McpException with standard .NET exceptions CRITICAL INSIGHT (Phase 8): Discovered that McpException usage was unnecessary complexity. Since we catch ALL exceptions before the MCP SDK sees them and return JSON responses, the SDK never encounters McpException - it gets our JSON error response instead. CHANGES: - Removed McpException catch blocks from all 11 tool files - Updated ExcelToolsBase to throw ArgumentException (validation) and InvalidOperationException (state) - Replaced McpException throws in: * ExcelFileTool (5 throws) * ExcelConnectionTool (17 throws) * All 9 'unknown action' switch branches * ExcelWorksheetTool invalid visibility ARCHITECTURAL BENEFIT: - Removed unnecessary coupling to ModelContextProtocol.McpException - Single catch (Exception ex) block now handles all cases - Uses standard .NET exception types for clearer semantics: * ArgumentException for parameter validation * InvalidOperationException for invalid state - Simpler, more idiomatic C# code - Same error handling quality (descriptive JSON responses) REMAINING: - ~86 parameter validation throws still using McpException (will be caught as Exception) - These still work correctly but should be migrated to ArgumentException in future PRs - Build: 0 warnings, 0 errors RATIONALE: The MCP SDK wraps our tool invocations with try-catch. Our code catches exceptions BEFORE the SDK sees them and returns JSON with isError=true. Therefore, whether we throw McpException, ArgumentException, or any other Exception is irrelevant to the SDK - it receives JSON in all cases. Using standard .NET exceptions is cleaner, doesn't add SDK coupling, and makes the code more maintainable. * refactor: Complete migration to ArgumentException for ALL parameter validations Complete removal of McpException pattern across all 11 tool files: - ExcelDataModelTool: 23 throws (all measure/table/relationship parameters) - ExcelWorksheetTool: 16 throws (all sheet/color/visibility parameters) - ExcelVbaTool: 10 throws (all module/path parameters) - ExcelTableTool: 5 throws (all table/column/format parameters) - ExcelQueryTableTool: 10 throws (all query table parameters) - ExcelRangeTool: 2 throws (shift direction validation) - ExcelPivotTableTool: 4 throws (aggregation/sort direction validation) - ExcelPowerQueryTool: 12 throws (completed in prior batch) - ExcelNamedRangeTool: 9 throws (completed in prior batch) - ExcelConnectionTool: 17 throws (completed in prior batch) - ExcelFileTool: 5 throws (completed in prior batch) TOTAL MIGRATION: 117/~117 parameter validation throws replaced Architecture Insight: - Standard .NET exceptions (ArgumentException for invalid input) - Single catch (Exception ex) block handles all cases - No SDK coupling - clean separation of concerns - Build: 0 warnings, 0 errors (maintained throughout) Breaking Simplification: - Removed unnecessary McpException abstraction layer - SDK-side exception handling wraps all tool invocations - No behavior change to callers (still returns JSON with isError=true) - Cleaner, more maintainable code with standard .NET patterns * refactor: Simplify async command invocation by removing unnecessary lambda expressions * fix: update .gitignore to exclude Jekyll generated files * Fix MCP Server tests: tool discovery and enum completeness - Fixed ToolDiscoveryTests: Removed non-existent 'excel_batch' from expected tools list - Fixed ActionEnumCompletenessTests: Added FileAction.Open/Save/Close mappings to ActionExtensions - Fixed tool naming: Changed 'excel_conditional_format' to 'excel_conditionalformat' (matches naming convention) - Enum cleanup: Removed unused BatchAction enum and its ToActionString() mapping Test results: 60 passing (was 58), 9 failing (was 11) Remaining failures are pre-existing issues (Power Query COM errors, invalid session handling) * Update documentation: 12 tools with 168 operations - Added excel_conditionalformat tool (2 actions: add-rule, clear-rules) - Removed excel_batch from tool lists (doesn't exist) - Updated operation counts: 166 → 168 - Updated tool counts: 11 → 12 - Added conditional formatting examples to CLI README - Updated all READMEs: main, MCP Server, CLI, VS Code extension, GitHub pages - Updated range action counts: 45 → 43 (moved conditional formatting to dedicated tool) - Added excel_querytable (8 actions) to complete tool list - Updated excel_file action count: 3 → 6 (open, save, close, create-empty, close-workbook, test) * Delete incorrect MCP Server tests expecting exceptions Removed tests that expected McpException for invalid sessions/missing parameters. Per CRITICAL-RULES.md Rule 17, MCP tools should return JSON with success=false for business errors, NOT throw exceptions. Tests deleted: - DetailedErrorMessageTests.cs (entire file - 8 tests) - ExcelMcpServerTests.ExcelWorksheet_InvalidSession_ShouldThrowError Test results: 59 passing (was 60), 2 failing (was 9) Remaining 2 failures are legitimate Power Query COM errors (RPC failures) * Fix ExcelBatch async disposal and timeout handling - Replace synchronous Task.Wait() with async DisposeAsync pattern - Add proper CancellationTokenSource disposal in finally blocks - Use ConfigureAwait(false) for library code - Fix timeout handling to properly cancel operations - Add comprehensive disposal tests with timeout scenarios * Remove ExportAsync methods - consolidate with ViewAsync All three command types (PowerQuery, VBA, Connection) now use ViewAsync instead of ExportAsync to return code/data directly rather than writing to files. This simplifies the API by eliminating duplicate functionality. Changes: - Removed ExportAsync from IPowerQueryCommands, IVbaCommands, IConnectionCommands interfaces - Removed ExportAsync implementations from all three command files - Updated CLI commands to use ViewAsync instead (return code directly) - Updated MCP Server tools to use ViewAsync instead (return JSON with code) - Updated VBA test to use ViewAsync instead of ExportAsync - Updated POWERQUERY-FUTURE-STATE-SPEC.md to remove ExportAsync references Tests now verify code is returned in ViewAsync result rather than written to file. * WIP: CLI async-to-sync conversion attempt - reverting to clean state * Save WIP * Align MCP tooling with updated actions * - Refactored tools to reduce cognizant load on LLM - Fix stability issues when working on multiple files at the same time * Increase STA thread cleanup timeout from 3 to 10 seconds to prevent potential deadlocks during disposal * Refactor QueryTable handling to use unified PowerQueryHelpers - Updated QueryTableCommand to utilize PowerQueryHelpers for creating QueryTables. - Introduced ConnectionHelpers for retrieving connection strings and command texts. - Consolidated QueryTable creation options into PowerQueryHelpers.QueryTableCreateOptions. - Removed redundant methods and classes related to QueryTable options. - Enhanced error handling and validation for connection strings. - Updated tests to reflect changes in QueryTable creation and options application. * Revert "Refactor QueryTable handling to use unified PowerQueryHelpers" This reverts commit3603afcb71. * Switch PowerQuery flows to inline M code * Fix formula string formatting in JSON deserialization tests * Fix test failures: formula syntax, PowerQuery Update, and QueryTable API usage - Fixed JSON syntax in formula tests (removed extra quotes in array) - Fixed raw string literal quote handling in Excel formulas - Fixed PowerQuery Update to preserve worksheet data: * Capture QueryTable destination and ResultRange before deletion * Clear only ResultRange (not entire sheet) * Recreate at same location with clearEntireSheet: false * Eliminates legacy clearEntireSheet anti-pattern - Fixed QueryTable tests to pass M code directly instead of file paths: * Removed file I/O operations (7 occurrences) * Aligned with PowerQueryCommands.Create() API signature All 9 originally failing tests now pass (2 formula + 7 QueryTable/PowerQuery). * Include mcp.json in version control - Added .vscode/mcp.json to .gitignore exceptions - Tracks MCP server configuration for project * Add sheet movement and cross-workbook operations: Move, CopyToWorkbook, MoveToWorkbook
9.8 KiB
9.8 KiB
Session API Redesign - Implementation Notes
Date: 2025-11-13 Branch: feature/session-api-redesign-spec
Implementation Status
✅ Completed Foundation (Ready for Use)
-
SessionManager in Core (
src/ExcelMcp.Core/SessionManager.cs)- Maps
sessionId→IExcelBatchfor both MCP Server and CLI - Methods:
CreateSessionAsync,GetSession,SaveSessionAsync,CloseSessionAsync
- Maps
-
ExcelToolsBase Enhanced (
src/ExcelMcp.McpServer/Tools/ExcelToolsBase.cs)- Static
SessionManagerinstance accessible viaGetSessionManager() - New
WithSessionAsyncmethod for session-based operations - Legacy
WithBatchAsyncmarked[Obsolete]but still functional
- Static
-
excel_file Tool Updated (
src/ExcelMcp.McpServer/Tools/ExcelFileTool.cs)- ✅
Openaction - creates session, returnssessionId - ✅
Saveaction - persists changes for active session - ✅
Closeaction - closes session without saving - ✅ Tool description updated with session lifecycle guidance
- ✅
-
Build Status
- ✅ Solution builds successfully (0 errors, 0 warnings)
- ✅ Core and ComInterop unchanged (
IExcelBatch,batchIdpreserved)
🚧 In Progress - Tool Migration Pattern
Pattern for Migrating Tools:
// OLD (batchId + excelPath):
public static async Task<string> ExcelTool(
ToolAction action,
string excelPath, // ❌ Remove
string? param = null,
string? batchId = null) // ❌ Change to sessionId
private static async Task<string> MethodAsync(Commands commands, string excelPath, string? param, string? batchId)
{
var result = await ExcelToolsBase.WithBatchAsync(batchId, excelPath, save: false, ...);
}
// NEW (sessionId only):
public static async Task<string> ExcelTool(
ToolAction action,
string sessionId, // ✅ Required session ID
string? param = null)
private static async Task<string> MethodAsync(Commands commands, string sessionId, string? param)
{
var result = await ExcelToolsBase.WithSessionAsync(sessionId, async (batch) => ...);
}
Steps Per Tool:
- Update tool method signature: Remove
excelPath, changebatchId?→sessionId(required) - Update tool description: Remove batch guidance, add session requirement
- Update all private method signatures: Replace
excelPathandbatchIdwithsessionId - Replace
WithBatchAsync→WithSessionAsyncin all private methods - Remove
saveparameter (sessions handle save viaexcel_fileactions)
📋 Remaining Work
Tools Needing Migration (11 tools):
- ❌
ExcelPowerQueryTool.cs- 11 methods to migrate - ❌
ExcelWorksheetTool.cs - ❌
ExcelRangeTool.cs - ❌
ExcelTableTool.cs - ❌
ExcelVbaTool.cs - ❌
ExcelConnectionTool.cs - ❌
ExcelDataModelTool.cs - ❌
ExcelPivotTableTool.cs - ❌
ExcelNamedRangeTool.cs - ❌
ExcelQueryTableTool.cs - ❌
BatchSessionTool.cs- DELETE (replaced by excel_file Open/Save/Close)
CLI Migration:
- Add
session-open,session-save,session-closecommands - Update all feature commands to accept
--session-idinstead of--batch-id - Map CLI
--session-id→ CorebatchIdinternally
Testing:
- Update MCP Server tests to use
excel_fileopen/save/close workflow - Add session lifecycle tests
- Verify backwards compatibility during transition
Documentation:
- Update all tool descriptions
- Update README with session workflow examples
- Update MCP prompts to explain session lifecycle
- Delete batch-related documentation
Remaining TODOs
1. Core Layer - Keep IExcelBatch and batchId (NO CHANGES)
- Core layer will NOT be changed - keep all existing
IExcelBatchinterfaces andbatchIdparameters - Core layer maintains its internal batch-based implementation
- Only MCP Server and CLI will expose the session abstraction to users
2. Introduce SessionManager and wire excel_file open/save/close (Core + MCP Server)
- Add a
SessionManagerinsrc/ExcelMcp.Coreresponsible for session lifecycle (shared by MCP Server and CLI):- Internal
ConcurrentDictionary<string, IExcelBatch>_activeSessions(Core still uses IExcelBatch) CreateSession(string filePath)→ opens viaExcelSession.BeginBatchAsyncand returnssessionIdGetSession(string sessionId)→ returnsIExcelBatch?SaveSession(string sessionId)→ callsSaveon the batchCloseSession(string sessionId)→ callsDisposeAsync, removes entry
- Internal
- Extend
FileActioninExcelFileToolwithOpen,Save, andClose - Update
ExcelFile(...)tool method to:open→ callSessionManager.CreateSession(filePath)and serialize a JSON response includingsessionId,filePath, andsuggestedNextActionssave→ callSessionManager.SaveSession(sessionId)and return success/failure JSONclose→ callSessionManager.CloseSession(sessionId)and return success/failure JSON (never saves)
3. Convert MCP tools to sessionId parameter (map to Core's batchId)
- For each MCP tool in
src/ExcelMcp.McpServer/Tools/**(connections, datamodel, file, namedrange, pivottable, powerquery, querytable, range, table, vba, worksheet, etc.):- Remove
excelPathparameter except forexcel_fileopen/create-emptyactions - Remove optional
batchIdparameter - Add
[Required] string sessionIdparameter (exposed to LLMs as "sessionId") - Inside tool methods, map
sessionId→batchIdwhen calling Core commands:// MCP tool accepts sessionId from LLM public static async Task<string> ExcelPowerQuery( PowerQueryAction action, string? sessionId = null, // ✅ Exposed to LLMs as sessionId ...) { // Get batch from session manager using sessionId var batch = SessionManager.GetSession(sessionId); if (batch == null) { return JsonSerializer.Serialize(new OperationResult { Success = false, ErrorMessage = $"Session '{sessionId}' not found", IsError = true }); } // Core still uses IExcelBatch with its internal batchId return await batch.ExecuteAsync((ctx, ct) => { return await commands.ImportAsync(batch, queryName, mCodeFile); }); }
- Remove
- Ensure business errors are returned as JSON and
McpExceptionis only used for protocol/validation errors
4. Remove batch infrastructure (BatchSessionTool, WithBatchAsync, CLI BatchCommands)
- Delete
src/ExcelMcp.McpServer/Tools/BatchSessionTool.csand remove any references to it. - Delete
src/ExcelMcp.CLI/Commands/BatchCommands.csand remove corresponding wiring and help text fromsrc/ExcelMcp.CLI/Program.cs. - Remove
ExcelToolsBase.WithBatchAsyncand anyif (batchId != null) { ... } else { ... }dual-path logic. - Rename any remaining
_activeBatchesfields to_activeSessions(or remove entirely if replaced bySessionManager).
5. Update CLI to use --session-id parameter (map to Core's batchId)
- Add or extend CLI file commands to mirror the MCP
excel_fileactions (open,save,close) and print/acceptsessionIdfor scripting - Update all feature CLI commands to accept
--session-idparameter (exposed to users as "session-id") - Inside CLI commands, map
--session-id→batchIdwhen calling Core:// CLI accepts --session-id from user public int Import(string[] args) { var sessionId = args.GetOption("--session-id"); // ✅ CLI uses sessionId // Map sessionId → batchId for Core layer var task = Task.Run(async () => { await using var batch = await ExcelSession.BeginBatchAsync( filePath, batchId: sessionId); // ✅ Core still uses batchId return await _commands.ImportAsync(batch, queryName, mCodeFile); }); } - Ensure CLI no longer creates temporary batches/sessions implicitly; instead, users/LLMs call
openfirst, then operate, thenclose
6. Update tests for session lifecycle and adjust existing coverage
- ComInterop/Core tests:
- Keep all existing
IExcelBatch/BeginBatchAsyncreferences - Core layer unchanged - Keep behavior and patterns identical (same Excel COM semantics, timeouts, etc.)
- Keep all existing
- MCP tests:
- Rewrite flows that currently use
excel_batchto useexcel_fileopen/save/closeandsessionId - Add tests for:
excel_file(open)returning a validsessionIdexcel_file(save)succeeds for a valid sessionexcel_file(close)closes without saving and returns success- Operations with missing/invalid
sessionIdreturn structured JSON errors - Read-only workflows: open → read → close (no save)
- Discard workflows: open → modify → close (no save)
- Rewrite flows that currently use
7. Update docs and prompts for session-only workflows
- Delete batch-related prompt artifacts:
src/ExcelMcp.McpServer/Prompts/Content/excel_batch.md(or equivalent).ExcelBatchModePromptsusage and any batch-mode guidance.
- Update
excel_filedocumentation and tool descriptions to:- Emphasize the required open/save/close lifecycle.
- Make clear that
closenever saves andsaveis an explicit action.
- Update all tool
[Description]attributes to:- Describe
sessionIdas a required parameter. - Remove references to
excelPathfor non-file tools.
- Describe
- Update
README.md,examples/*.ps1,examples/*.sh, and any relevant docs to show:- Default workflow:
open → operations(sessionId) → save → close. - Read-only workflow:
open → read → close. - Discard workflow:
open → modify → close(no save).
- Default workflow:
This file captures the current implementation state of the Session API redesign and the remaining work needed to fully implement the "sessions only" Open/Save/Close model described in specs/SESSION-API-REDESIGN-SPEC.md.
Use it as a checklist while completing the refactor across ComInterop, Core, MCP Server, CLI, tests, and documentation.