Files
sbroenne__mcp-server-excel/specs/CHART-API-SPECIFICATION.md
T
Stefan Broenner 6b3a557870 refactor: Migrate entire codebase to exception-based pattern (void mutations, no OperationResult suppression) (#233)
* Refactor MCP tools to share error handling

* Refactor Core Commands to remove exception suppression

Per CRITICAL-RULES.md Rule 1b:
- Removed catch blocks that suppress exceptions by returning OperationResult { Success = false }
- Let exceptions propagate naturally through batch.Execute()
- Kept finally blocks for COM resource cleanup
- Removed orphaned try blocks from methods with no COM cleanup needs

Modified files:
- RangeCommands.AutoFit.cs (2 methods)
- RangeCommands.Advanced.cs (5 methods)
- RangeCommands.Validation.cs (3 methods)
- RangeCommands.Formatting.cs (3 methods)
- PivotTableCommands.GrandTotals.cs (1 method)
- OlapPivotTableFieldStrategy.cs (2 methods)
- RegularPivotTableFieldStrategy.cs (1 method)

Architecture: batch.Execute() ALREADY captures exceptions via TaskCompletionSource.
Inner try-catch suppresses exceptions and loses stack context.
Exception propagation now happens at correct layer (batch, not method).

* Update copilot-instructions: Add surgical testing guidance and exception propagation pattern

CRITICAL additions:
- Integration tests take 45+ MINUTES for full suite
- ALWAYS use surgical testing with Feature filters (2-5 min per feature)
- Core Commands NEVER wrap batch.Execute() in catch blocks
- Let exceptions propagate naturally - batch.Execute() handles via TaskCompletionSource
- ONLY use finally blocks for COM cleanup, NO catch blocks returning OperationResult

This prevents 45+ minute test runs and documents correct exception handling pattern per CRITICAL-RULES.md Rule 1b.

* Remove additional violating catch blocks from PivotTable commands

Found and removed 7 more catch blocks that violated Rule 1b:
- PivotTableCommands.Subtotals.SetSubtotals
- PivotTableCommands.Grouping.GroupByDate
- PivotTableCommands.Grouping.GroupByNumeric
- PivotTableCommands.CalculatedFields.CreateCalculatedField
- PivotTableCommands.Analysis.GetData
- PivotTableCommands.Analysis.SetFieldFilter
- PivotTableCommands.Analysis.SortField

All were inside batch.Execute() and returning OperationResult { Success = false }.
Removed catch blocks, kept finally blocks for COM cleanup.
Exceptions now propagate naturally through batch.Execute().

* refactor(core): remove violating catch block from TableCommands.DataModel.cs

Removed catch block that violated Rule 1b (CRITICAL-RULES.md).
AddToDataModel now lets exceptions propagate through batch.Execute().

Pattern removed:
catch (Exception ex) {
    return new OperationResult { Success = false, ErrorMessage = ... };
}

Rationale:
- batch.Execute() captures exceptions via TaskCompletionSource
- Inner catch suppresses exceptions and loses stack context
- Exceptions should originate from batch layer, not method layer

Part of comprehensive audit removing all violating catch blocks from Core Commands.

* test(core): fix Range style tests to expect exceptions after architectural changes

Updated 2 tests to expect exceptions instead of OperationResult { Success = false }:
- SetStyle_InvalidStyleName_ThrowsException (was ReturnsError)
- GetStyle_InvalidRange_ThrowsException (was ReturnsError)

Why Changed:
After removing violating catch blocks (Rule 1b), SetStyle() and GetStyle()
no longer catch Excel COM exceptions. batch.Execute() captures exceptions via
TaskCompletionSource and re-throws them.

Pattern:
Methods WITHOUT pre-validation → Excel COM throws → batch.Execute() captures →
Exception propagates to caller → Test must expect exception

Verified Clean:
- Tests with pre-validation (Sheet.Move, NamedRange.CreateBulk) still expect
  Success=false correctly
- Tests with business rules (OLAP calculated fields) correctly expect Success=false
- Only methods letting Excel COM throw directly needed test updates

All 2 tests now pass with architectural fix in place.

* refactor: remove async wrappers and bulk operations

Remove ViewAsync, CreateBulk methods and update documentation

* refactor(chart): Convert Chart commands to exception-based pattern

- IChartCommands: Convert 14 method signatures (OperationResult/ChartSeriesResult/ChartListResult → void/SeriesInfo/List<ChartInfo>)
- IChartStrategy: Convert 3 data operation signatures (SetSourceRange→void, AddSeries→SeriesInfo, RemoveSeries→void)
- RegularChartStrategy: Remove OperationResult wrappers, return data types directly
- PivotChartStrategy: Convert error returns to throw NotSupportedException with helpful messages
- ChartCommands implementations: Update DataSource/Lifecycle/Appearance to match new signatures
- Remove unnecessary Models using directives

CRUD ops return void (throw on error), Query ops return data types, Exceptions replace Success/ErrorMessage wrappers.
Pattern validated: Core commands build successfully with 0 errors/warnings.

* fix(chart): Fix COM cleanup in PivotChartStrategy to use finally blocks

- SetSourceRange: Move COM Release to finally block ensuring cleanup even if exception occurs
- AddSeries: Move COM Release to finally block ensuring cleanup even if exception occurs
- RemoveSeries: Move COM Release to finally block ensuring cleanup even if exception occurs
- Declare COM objects as nullable dynamic? variables before try-catch-finally

Prevents COM object leaks when exceptions occur during PivotTable name retrieval.
Finally blocks guarantee cleanup happens regardless of try/catch outcome.
Follows proper COM interop resource management pattern from excel-com-interop.instructions.md.

* refactor(chart): Remove empty catch blocks from PivotChartStrategy

- GetInfo: Remove empty catch blocks, keep only try-finally for COM cleanup
- GetDetailedInfo: Remove empty catch block, keep only try-finally for COM cleanup

Empty catch blocks are unnecessary when finally blocks handle cleanup.
Finally blocks execute regardless of exception occurrence.
Cleaner code pattern: try { operations } finally { cleanup }

* refactor(chart): Fix COM cleanup in RegularChartStrategy

- GetInfo: Move seriesCollection to try-finally without empty catch
- GetDetailedInfo: Move seriesCollection to try-finally, keep only justified catch blocks for optional properties
- Remove COM Release from try block, move to finally block
- Keep catch blocks only where fallback values make sense (HasLegend=false, etc.)

Ensures COM objects always released even if exceptions occur.
Empty catch blocks removed where no fallback action needed.

* refactor(chart): Simplify PivotChartStrategy exception messages

- SetSourceRange: Remove COM object lookup, just throw NotSupportedException
- AddSeries: Remove COM object lookup, just throw NotSupportedException
- RemoveSeries: Remove COM object lookup, just throw NotSupportedException

LLM already knows context - no need for complex COM operations to get PivotTable name.
Simpler, cleaner, no COM cleanup needed for error-only code paths.

* refactor(chart): Convert CLI ChartCommand to exception-based pattern

- Removed Result wrapper pattern from all Execute methods
- Added try-catch exception handling with proper exit codes (0=success, 1=error)
- Query operations: return data via WriteJson
- CRUD operations: void with WriteInfo success messages
- Removed WriteResult helper method (no longer needed)
- Removed unused Models using statement
- All 14 Execute methods converted:
  * ExecuteList (List<ChartInfo>)
  * ExecuteRead (ChartInfoResult)
  * ExecuteCreateFromRange (ChartCreateResult)
  * ExecuteCreateFromPivotTable (ChartCreateResult)
  * ExecuteDelete (void)
  * ExecuteMove (void)
  * ExecuteSetSourceRange (void)
  * ExecuteAddSeries (SeriesInfo)
  * ExecuteRemoveSeries (void)
  * ExecuteSetChartType (void)
  * ExecuteSetTitle (void)
  * ExecuteSetAxisTitle (void)
  * ExecuteShowLegend (void)
  * ExecuteSetStyle (void)

Part of Phase 2.2: Chart CLI Updates

* refactor(chart): Convert MCP ExcelChartTool to exception-based pattern

- Removed Result wrapper anonymous object serialization
- Query operations: serialize data types directly (List<ChartInfo>, ChartInfoResult, ChartCreateResult, SeriesInfo)
- CRUD operations (void): return success message JSON after completion
- Exception handling: ExecuteToolAction wrapper catches and serializes exceptions
- All 14 actions converted:
  * List → serialize List<ChartInfo>
  * Read → serialize ChartInfoResult
  * CreateFromRange → serialize ChartCreateResult
  * CreateFromPivotTable → serialize ChartCreateResult
  * Delete → success message JSON
  * Move → success message JSON
  * SetSourceRange → success message JSON
  * AddSeries → serialize SeriesInfo
  * RemoveSeries → success message JSON
  * SetChartType → success message JSON
  * SetTitle → success message JSON
  * SetAxisTitle → success message JSON
  * ShowLegend → success message JSON
  * SetStyle → success message JSON

Part of Phase 2.3: Chart MCP Tool Updates

* refactor(chart): Convert MCP ExcelChartTool to exception-based pattern

- Removed Result wrapper anonymous object serialization
- Query operations: serialize data types directly (List<ChartInfo>, ChartInfoResult, ChartCreateResult, SeriesInfo)
- CRUD operations (void): return success message JSON after completion
- Exception handling: ExecuteToolAction wrapper catches and serializes exceptions
- All 14 actions converted:
  * List → serialize List<ChartInfo>
  * Read → serialize ChartInfoResult
  * CreateFromRange → serialize ChartCreateResult
  * CreateFromPivotTable → serialize ChartCreateResult
  * Delete → success message JSON
  * Move → success message JSON
  * SetSourceRange → success message JSON
  * AddSeries → serialize SeriesInfo
  * RemoveSeries → success message JSON
  * SetChartType → success message JSON
  * SetTitle → success message JSON
  * SetAxisTitle → success message JSON
  * ShowLegend → success message JSON
  * SetStyle → success message JSON

Part of Phase 2.3: Chart MCP Tool Updates

* refactor(namedrange): Convert NamedRange commands to exception pattern (all layers)

Core Changes:
- INamedRangeCommands: List returns List<NamedRangeInfo>, Read returns NamedRangeValue, CRUD operations void
- Added NamedRangeValue class (POCO for Read operation)
- All methods throw exceptions on error (InvalidOperationException, ArgumentException)
- Parameter validation enforced (255 char limit, no duplicates)

CLI Changes:
- All Execute methods use try-catch with exit codes (0=success, 1=exception, -1=validation)
- List/Get serialize data directly via WriteJson
- Create/Update/Delete/Write use WriteInfo for success messages
- Removed WriteResult helper method
- Removed unused using directive

MCP Server Changes:
- List serializes List<NamedRangeInfo> directly
- Read serializes NamedRangeValue directly
- Write/Create/Update/Delete wrap void operations in lambda with dummy return
- All CRUD ops return success message JSON with operation-specific messages
- Delete includes workflowHint about #NAME? errors

Test Changes (12 tests converted, all passing):
- Lifecycle.cs: 4 tests - List accesses data directly, Create/Delete void with verification
- Validation.cs: 6 tests - Parameter validation with Assert.Throws for errors
- Values.cs: 3 tests - Write/Read void/data operations with verification

All layers build successfully with 0 errors, 0 warnings
All 12 NamedRange tests passing (100%)

Phase 3 (NamedRange) complete - 6 Core methods + 12 tests converted

* refactor(conditionalformat): Convert ConditionalFormatting commands to exception pattern

Core Changes:
- IConditionalFormattingCommands: AddRule and ClearRules return void
- All methods throw exceptions on error (InvalidOperationException, ArgumentException)
- Removed OperationResult wrappers, use dummy return 0 for batch.Execute
- Removed unused using directives

CLI Changes:
- ExecuteAddRule/ExecuteClearRules use try-catch with exit codes (0=success, 1=exception, -1=validation)
- WriteInfo for success messages
- Removed WriteResult helper method
- Removed unused using directive

MCP Server Changes:
- AddRule/ClearRules wrap void operations in lambda with dummy return
- Return success message JSON with operation-specific messages
- Keep workflowHints for formatting verification guidance

All layers build successfully with 0 errors, 0 warnings
No tests exist for ConditionalFormatting commands

Phase 4.1 (ConditionalFormatting) complete - 2 Core methods converted

* Refactor: Convert Connection, File, VBA subsystems from OperationResult to exception-throwing pattern

- Connection: IConnectionCommands methods now return void, throw on error
- File: CreateEmpty returns void, throws on error
- VBA: Trust, Import, Delete, Run operations throw exceptions
- CLI layer: Updated error handling for void operations
- MCP layer: Responses serialized as {success: true} for void operations
- Test infrastructure: Updated 14 test helper files to check exceptions

Mutation operations now follow .NET convention of throwing exceptions instead of returning error-wrapped results. Read operations continue returning typed results.

All three layers (Core, CLI, MCP) coordinated for consistent behavior.

* Merge branch 'feature/shared-tool-error-handling' of https://github.com/sbroenne/mcp-server-excel into feature/shared-tool-error-handling

* Refactor: Convert Sheet subsystem from OperationResult to exception-throwing pattern

Convert 12 Sheet mutation methods to void signatures across all layers:
- ISheetCommands: Create, Rename, Copy, Delete, Move, CopyToWorkbook, MoveToWorkbook, SetTabColor, ClearTabColor, SetVisibility, Show, Hide, VeryHide
- SheetCommands (Core): All implementations use batch.Execute() with return 0, exceptions propagate
- SheetCommand (CLI): All void operations wrapped in try-catch with success/error messages
- ExcelWorksheetTool (MCP): All 10 void methods use WithSession() + return 0 + JSON serialization

Production code compiles cleanly. MCP smoke test: PASSED. Tests require separate refactoring for void method assertions.

* Refactor: Convert Table subsystem from OperationResult to exception-throwing pattern (17 void methods, Core+CLI+MCP layers)

- ITableCommands: 17 mutation methods converted to void signatures with exception documentation
- Core layer: 17 implementations across 7 partial files use batch.Execute() return 0 pattern
- CLI layer: 10 void handlers use try-catch-serialize pattern with success/error responses
- MCP layer: 10 void tool methods use try-catch-JSON pattern (fixed 8 broken WithSession<T> calls)
- All production layers compile cleanly: 0 errors, 0 warnings
- MCP smoke test PASSED: all 11 tools working correctly
- Test layer errors (44) deferred: void assertion issues to be fixed separately
- Pattern matches Sheet/Connection/File/VBA subsystem conversions

* feat: Convert PowerQuery subsystem mutation methods to void pattern (Delete, Create, Update, LoadTo, RefreshAll)

* Convert DataModel subsystem to exception-throwing pattern

- Interface: 7 void mutation methods (DeleteMeasure, DeleteRelationship, Refresh (2 overloads), CreateMeasure, UpdateMeasure, CreateRelationship, UpdateRelationship)
- Core: Converted Write.cs (6 mutations) and Refresh.cs (2 methods) to void pattern with batch.Execute((ctx, ct) => { ...code...; return 0; })
- CLI: 7 handlers with try-catch-console JSON output
- MCP: 7 tool methods with try-catch-JSON error handling
- Removed unused Core.Models imports from Write.cs and Refresh.cs
- Build: 0 errors, 0 warnings across Core, CLI, MCP layers

Note: Test failures expected (44 in Core, 50+ in other test files due to void assertion issues) - these are deferred per testing strategy (Rule 14)

* Merge branch 'feature/shared-tool-error-handling' of https://github.com/sbroenne/mcp-server-excel into feature/shared-tool-error-handling

* fix: Complete void test migration - 84/84 errors fixed

Complete migration of 84 test compilation errors to exception-based pattern.
This completes the integration of void Execute infrastructure with test layer updates.

✅ BUILD VERIFIED: 0 errors, 0 warnings
✅ PRE-COMMIT PASSED: All quality gates passing

Test Migrations (84 total):
- Sheet tests: 19 errors (Move, TabColor, Visibility, Lifecycle)
- Helper fixtures: 4 errors (PowerQuery, Table, DataModel, PivotTable)
- PowerQueryCommandsTests: 19 errors (Create, Update, Delete, LoadTo calls)
- DataModelCommandsTests: 12 errors (CreateMeasure, UpdateMeasure, CreateRelationship, DeleteRelationship)
- DataModelTestsFixture: 1 error (var assignment from void CreateMeasure)
- TableCommandsTests: 2 errors (Create calls in tests and fixture)
- PivotTableCommandsTests: 3 errors (Create calls from Table/DataModel)
- PivotTableCommandsTests.OlapFields: 2 errors (CreateMeasure calls)

Pattern Applied (Two types):
1. CS0815 'Cannot assign void': Remove 'var result =' and 'Assert.True(result.Success)' lines
2. Remove unnecessary success checks - void methods throw on error instead

Infrastructure:
- void Execute(Action<ExcelContext, CancellationToken>) in IExcelBatch/ExcelBatch
- Delegates to Execute<T>(Func<...>) returning dummy 0
- Non-breaking enhancement for clean void method support

Verification:
✅ Build: 0 errors, 0 warnings
✅ Pre-commit checks: All passed
  - COM leak detection: 0 leaks
  - Success flag validation: All consistent
  - MCP implementation coverage: 100%
  - MCP server smoke test: Passed
✅ Feature branch active (feature/shared-tool-error-handling)

* docs: Add void test migration completion summary

* refactor: Convert 11 Range void mutations to fire-and-forget pattern across all layers

Complete refactoring of Range command mutations to void pattern:

CORE LAYER (11 void mutations):
- RangeCommands.Search.cs: Replace, Sort
- RangeCommands.Formatting.cs: SetStyle, FormatRange
- RangeCommands.Validation.cs: ValidateRange, RemoveValidation
- RangeCommands.AutoFit.cs: AutoFitColumns, AutoFitRows
- RangeCommands.Advanced.cs: MergeCells, UnmergeCells, SetCellLock

INTERFACE:
- IRangeCommands.cs: All 11 signatures changed from OperationResult to void

MCP TOOL LAYER:
- ExcelRangeTool.cs: All 11 calls updated to use WithSession<object?>() pattern
  * Explicit type parameter resolves CS0411 type inference on void returns
  * Returns { Success = true } JSON directly for all void mutations
  * Lambda formatting fixed (IDE0055 compliance)

CLI LAYER:
- RangeCommand.cs: All 11 command handlers adapted
  * Removed var result = ... assignments
  * Call void methods directly
  * Return { Success = true } JSON on completion

TEST LAYER (14+ files):
- RangeCommandsTests: All test files updated
  * Removed var result = ... assignments
  * Removed Assert.True(result.Success, ...) assertions
  * Void methods throw on failure; silence = success

BUILD STATUS: ✅ Clean - 0 errors, 0 warnings

LEGITIMATE OperationResult METHODS (NOT CONVERTED):
- SetValues, SetFormulas, Copy, SetNumberFormat, AddHyperlink
- These data mutations need validation and result reporting

PATTERN:
- Void mutations: Fire-and-forget with exception-based error handling
- Exceptions propagate naturally through batch.Execute() layer
- No OperationResult wrapping needed for simple write operations

* chore: Remove unused imports and temporary documentation files

- Clean up unused using directives across Table and PivotTable commands
- Remove temporary documentation files
- Maintain code style consistency

* fix: Update MartinCostello.Logging.XUnit package version to 0.7.0

* fix(chart): Suppress CodeQL false positives for dynamic COM interop in Strategy pattern

- Add pragma warning disable CS8604 for all Strategy method calls
- CodeQL cannot verify dynamic Excel COM objects have required methods at compile-time
- All methods verified to exist in both RegularChartStrategy and PivotChartStrategy
- False positives: SetSourceRange, AddSeries, RemoveSeries, GetInfo, GetDetailedInfo
- Addresses code scanning alerts #2631-2635
2025-11-23 17:32:04 +00:00

32 KiB

Excel Chart API Specification

Comprehensive specification for Excel chart operations - creating and managing Regular Charts and PivotCharts

Implementation Status

Phase 1 (MVP): 🚧 IN PROGRESS (As of November 22, 2025)

  • 🚧 Core interface and strategy pattern design
  • 🚧 Regular Chart and PivotChart lifecycle operations
  • 🚧 MCP Server integration planning
  • ⏸️ CLI commands (after core implementation)
  • ⏸️ Integration tests (after core implementation)

Target: 20-25 core operations covering 90% of chart automation use cases


Executive Summary

This specification defines a Chart API for ExcelMcp that provides complete chart lifecycle management, data source configuration, and appearance customization through Excel COM automation. The API handles two fundamentally different chart types:

  1. Regular Charts - Static charts created from Excel ranges/tables
  2. PivotCharts - Dynamic interactive charts linked to PivotTables

Key Design Decisions

  1. COM-Backed Only - Every operation uses native Excel COM Chart objects
  2. Two-Type Strategy Pattern - Regular vs PivotChart behavior differences handled transparently
  3. Unified API - Same method signatures for both chart types (strategy handles implementation)
  4. Complete ChartType Enum - All 70+ Excel chart types exposed (grouped by category)
  5. Positioning Required - Both Regular and PivotCharts require left/top/width/height coordinates

Goals

  1. Complete Lifecycle - Create, read, move, delete charts of both types
  2. Data Source Management - Set ranges, add/remove series (Regular), sync with PivotTable (PivotCharts)
  3. Appearance Control - Chart types, titles, legends, styles
  4. LLM-Friendly - Clear error messages guide workflow when operations differ between types
  5. 90% Coverage - Core operations satisfy most automation scenarios

Excel Chart Architecture

What Are Excel Charts?

Excel charts are visual representations of data that provide:

  • Multiple chart types (column, line, pie, scatter, etc.)
  • Embedded positioning on worksheets (left, top, width, height)
  • Data series from ranges or PivotTable fields
  • Titles, legends, axes, and styling
  • Export and presentation capabilities

The Two Chart Types (Behavioral Difference)

Regular Charts

  • Data Source: Excel ranges or tables
  • Behavior: Static - updates only when source range updates
  • Series Management: Explicit via SeriesCollection
  • Creation: Shapes.AddChart() or ChartObjects.Add()
  • Use Cases: Reports, dashboards, static analysis

PivotCharts

  • Data Source: PivotTable/PivotCache
  • Behavior: Dynamic - updates automatically when PivotTable changes
  • Series Management: Automatic sync with PivotTable value fields
  • Creation: PivotCache.CreatePivotChart() or Shapes.AddChart2() + link
  • Use Cases: Interactive analysis, drill-down reports, OLAP cubes

Excel COM Object Model

Core Objects

// Worksheet-level chart access
dynamic shapes = worksheet.Shapes;
dynamic chartObjects = worksheet.ChartObjects;

// Regular Chart creation (Modern API - Excel 2010+)
dynamic shape = shapes.AddChart(
    XlChartType: 51,  // xlColumnClustered
    Left: 100,
    Top: 100,
    Width: 400,
    Height: 300
);
dynamic chart = shape.Chart;

// PivotChart creation (from PivotTable)
dynamic pivotCache = pivotTable.PivotCache();
dynamic pivotChart = pivotCache.CreatePivotChart(
    Destination: worksheet.Range["H1"]  // Top-left position
);

// Chart object hierarchy
dynamic chart = chartObject.Chart;  // OR shape.Chart
dynamic seriesCollection = chart.SeriesCollection();
dynamic series = seriesCollection.Item(1);

Chart vs ChartObject

  • ChartObject - Wrapper providing positioning (Left, Top, Width, Height)
  • Chart - The actual chart with data, type, titles, legends
  • Both Regular and PivotCharts use this dual-object pattern

Proposed Chart API Design

Design Principles

  1. COM-Backed Only: Every method uses native Excel COM Chart operations
  2. Strategy Pattern: IChartStrategy with RegularChartStrategy and PivotChartStrategy
  3. Unified API: Same method signatures - strategy handles implementation differences
  4. Complete Enum: All 70+ chart types exposed, grouped by category for readability
  5. Error Guidance: Clear messages guide LLMs when operations differ between types

Phase 1: Core Operations (MVP)

public interface IChartCommands
{
    // === LIFECYCLE OPERATIONS ===
    
    /// <summary>
    /// Lists all charts in workbook (Regular and PivotCharts)
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <returns>List of charts with names, types, sheets, positions, data sources</returns>
    ChartListResult List(IExcelBatch batch);
    
    /// <summary>
    /// Gets complete chart configuration
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart (or shape name)</param>
    /// <returns>Chart type, data source, series info, position, styling</returns>
    ChartInfoResult Read(IExcelBatch batch, string chartName);
    
    /// <summary>
    /// Creates a Regular Chart from an Excel range
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="sheetName">Worksheet name for chart placement</param>
    /// <param name="sourceRange">Source data range (e.g., "A1:D10")</param>
    /// <param name="chartType">Chart type from ChartType enum</param>
    /// <param name="left">Left position in points</param>
    /// <param name="top">Top position in points</param>
    /// <param name="width">Width in points (default: 400)</param>
    /// <param name="height">Height in points (default: 300)</param>
    /// <param name="chartName">Optional name for the chart</param>
    /// <returns>Created chart name and configuration</returns>
    ChartCreateResult CreateFromRange(IExcelBatch batch, 
        string sheetName, string sourceRange, 
        ChartType chartType,
        double left, double top, 
        double width = 400, double height = 300,
        string? chartName = null);
    
    /// <summary>
    /// Creates a PivotChart from an existing PivotTable
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="pivotTableName">Name of the PivotTable</param>
    /// <param name="sheetName">Worksheet name for chart placement</param>
    /// <param name="chartType">Chart type from ChartType enum</param>
    /// <param name="left">Left position in points</param>
    /// <param name="top">Top position in points</param>
    /// <param name="width">Width in points (default: 400)</param>
    /// <param name="height">Height in points (default: 300)</param>
    /// <param name="chartName">Optional name for the chart</param>
    /// <returns>Created PivotChart name and linked PivotTable</returns>
    ChartCreateResult CreateFromPivotTable(IExcelBatch batch,
        string pivotTableName, string sheetName,
        ChartType chartType,
        double left, double top,
        double width = 400, double height = 300,
        string? chartName = null);
    
    /// <summary>
    /// Deletes a chart (Regular or PivotChart)
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart to delete</param>
    /// <returns>Operation result</returns>
    OperationResult Delete(IExcelBatch batch, string chartName);
    
    /// <summary>
    /// Moves/resizes a chart
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart</param>
    /// <param name="left">New left position in points (null = no change)</param>
    /// <param name="top">New top position in points (null = no change)</param>
    /// <param name="width">New width in points (null = no change)</param>
    /// <param name="height">New height in points (null = no change)</param>
    /// <returns>Operation result with new position</returns>
    OperationResult Move(IExcelBatch batch, string chartName,
        double? left = null, double? top = null,
        double? width = null, double? height = null);
    
    // === DATA SOURCE OPERATIONS ===
    
    /// <summary>
    /// Sets data source range for Regular Charts
    /// PivotCharts: Returns error guiding to excel_pivottable
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart</param>
    /// <param name="sourceRange">New source range (e.g., "Sheet1!A1:D10")</param>
    /// <returns>Operation result</returns>
    OperationResult SetSourceRange(IExcelBatch batch, string chartName, string sourceRange);
    
    /// <summary>
    /// Adds a data series to Regular Charts
    /// PivotCharts: Returns error guiding to excel_pivottable(action: 'add-value-field')
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart</param>
    /// <param name="seriesName">Name for the series</param>
    /// <param name="valuesRange">Range containing Y values (e.g., "Sheet1!B2:B10")</param>
    /// <param name="categoryRange">Optional range for X values/categories</param>
    /// <returns>Series information</returns>
    ChartSeriesResult AddSeries(IExcelBatch batch, string chartName,
        string seriesName, string valuesRange, string? categoryRange = null);
    
    /// <summary>
    /// Removes a data series from Regular Charts
    /// PivotCharts: Returns error guiding to excel_pivottable(action: 'remove-field')
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart</param>
    /// <param name="seriesIndex">1-based index of series to remove</param>
    /// <returns>Operation result</returns>
    OperationResult RemoveSeries(IExcelBatch batch, string chartName, int seriesIndex);
    
    // === APPEARANCE OPERATIONS ===
    
    /// <summary>
    /// Changes chart type (works for both Regular and PivotCharts)
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart</param>
    /// <param name="chartType">New chart type from ChartType enum</param>
    /// <returns>Operation result</returns>
    OperationResult SetChartType(IExcelBatch batch, string chartName, ChartType chartType);
    
    /// <summary>
    /// Sets chart title
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart</param>
    /// <param name="title">Chart title text (empty to hide title)</param>
    /// <returns>Operation result</returns>
    OperationResult SetTitle(IExcelBatch batch, string chartName, string title);
    
    /// <summary>
    /// Sets axis title
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart</param>
    /// <param name="axis">Axis type (Primary, Secondary, Category, Value)</param>
    /// <param name="title">Axis title text</param>
    /// <returns>Operation result</returns>
    OperationResult SetAxisTitle(IExcelBatch batch, string chartName, 
        ChartAxisType axis, string title);
    
    /// <summary>
    /// Shows or hides chart legend
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart</param>
    /// <param name="visible">True to show legend, false to hide</param>
    /// <param name="position">Legend position (optional)</param>
    /// <returns>Operation result</returns>
    OperationResult ShowLegend(IExcelBatch batch, string chartName, 
        bool visible, LegendPosition? position = null);
    
    /// <summary>
    /// Applies a chart style
    /// </summary>
    /// <param name="batch">Excel batch session</param>
    /// <param name="chartName">Name of the chart</param>
    /// <param name="styleId">Style number (1-48)</param>
    /// <returns>Operation result</returns>
    OperationResult SetStyle(IExcelBatch batch, string chartName, int styleId);
}

// === SUPPORTING TYPES ===

/// <summary>
/// Excel chart types - All 70+ values grouped by category
/// Excel COM: XlChartType enumeration
/// Reference: https://learn.microsoft.com/office/vba/api/excel.xlcharttype
/// </summary>
public enum ChartType
{
    // === COLUMN CHARTS ===
    ColumnClustered = 51,           // xlColumnClustered
    ColumnStacked = 52,             // xlColumnStacked
    ColumnStacked100 = 53,          // xlColumnStacked100
    Column3DClustered = 54,         // xl3DColumnClustered
    Column3DStacked = 55,           // xl3DColumnStacked
    Column3DStacked100 = 56,        // xl3DColumnStacked100
    Column3D = -4100,               // xl3DColumn
    
    // === BAR CHARTS ===
    BarClustered = 57,              // xlBarClustered
    BarStacked = 58,                // xlBarStacked
    BarStacked100 = 59,             // xlBarStacked100
    Bar3DClustered = 60,            // xl3DBarClustered
    Bar3DStacked = 61,              // xl3DBarStacked
    Bar3DStacked100 = 62,           // xl3DBarStacked100
    
    // === LINE CHARTS ===
    Line = 4,                       // xlLine
    LineStacked = 63,               // xlLineStacked
    LineStacked100 = 64,            // xlLineStacked100
    LineMarkers = 65,               // xlLineMarkers
    LineMarkersStacked = 66,        // xlLineMarkersStacked
    LineMarkersStacked100 = 67,     // xlLineMarkersStacked100
    Line3D = -4101,                 // xl3DLine
    
    // === PIE CHARTS ===
    Pie = 5,                        // xlPie
    Pie3D = -4102,                  // xl3DPie
    PieOfPie = 68,                  // xlPieOfPie
    PieExploded = 69,               // xlPieExploded
    PieExploded3D = 70,             // xl3DPieExploded
    BarOfPie = 71,                  // xlBarOfPie
    
    // === SCATTER (XY) CHARTS ===
    XYScatter = -4169,              // xlXYScatter
    XYScatterSmooth = 72,           // xlXYScatterSmooth
    XYScatterSmoothNoMarkers = 73,  // xlXYScatterSmoothNoMarkers
    XYScatterLines = 74,            // xlXYScatterLines
    XYScatterLinesNoMarkers = 75,   // xlXYScatterLinesNoMarkers
    
    // === AREA CHARTS ===
    Area = 1,                       // xlArea
    AreaStacked = 76,               // xlAreaStacked
    AreaStacked100 = 77,            // xlAreaStacked100
    Area3D = -4098,                 // xl3DArea
    Area3DStacked = 78,             // xl3DAreaStacked
    Area3DStacked100 = 79,          // xl3DAreaStacked100
    
    // === DOUGHNUT CHARTS ===
    Doughnut = -4120,               // xlDoughnut
    DoughnutExploded = 80,          // xlDoughnutExploded
    
    // === RADAR CHARTS ===
    Radar = -4151,                  // xlRadar
    RadarMarkers = 81,              // xlRadarMarkers
    RadarFilled = 82,               // xlRadarFilled
    
    // === SURFACE CHARTS ===
    Surface = 83,                   // xlSurface
    SurfaceWireframe = 84,          // xlSurfaceWireframe
    SurfaceTopView = 85,            // xlSurfaceTopView
    SurfaceTopViewWireframe = 86,   // xlSurfaceTopViewWireframe
    
    // === BUBBLE CHARTS ===
    Bubble = 15,                    // xlBubble
    Bubble3DEffect = 87,            // xlBubble3DEffect
    
    // === STOCK CHARTS ===
    StockHLC = 88,                  // xlStockHLC (High-Low-Close)
    StockOHLC = 89,                 // xlStockOHLC (Open-High-Low-Close)
    StockVHLC = 90,                 // xlStockVHLC (Volume-High-Low-Close)
    StockVOHLC = 91,                // xlStockVOHLC (Volume-Open-High-Low-Close)
    
    // === CYLINDER CHARTS ===
    CylinderBarClustered = 95,      // xlCylinderBarClustered
    CylinderBarStacked = 96,        // xlCylinderBarStacked
    CylinderBarStacked100 = 97,     // xlCylinderBarStacked100
    CylinderCol = 98,               // xlCylinderCol
    CylinderColClustered = 92,      // xlCylinderColClustered
    CylinderColStacked = 93,        // xlCylinderColStacked
    CylinderColStacked100 = 94,     // xlCylinderColStacked100
    
    // === CONE CHARTS ===
    ConeBarClustered = 102,         // xlConeBarClustered
    ConeBarStacked = 103,           // xlConeBarStacked
    ConeBarStacked100 = 104,        // xlConeBarStacked100
    ConeCol = 105,                  // xlConeCol
    ConeColClustered = 99,          // xlConeColClustered
    ConeColStacked = 100,           // xlConeColStacked
    ConeColStacked100 = 101,        // xlConeColStacked100
    
    // === PYRAMID CHARTS ===
    PyramidBarClustered = 109,      // xlPyramidBarClustered
    PyramidBarStacked = 110,        // xlPyramidBarStacked
    PyramidBarStacked100 = 111,     // xlPyramidBarStacked100
    PyramidCol = 112,               // xlPyramidCol
    PyramidColClustered = 106,      // xlPyramidColClustered
    PyramidColStacked = 107,        // xlPyramidColStacked
    PyramidColStacked100 = 108,     // xlPyramidColStacked100
    
    // === MODERN CHARTS (Excel 2016+) ===
    Treemap = 117,                  // xlTreemap
    Sunburst = 116,                 // xlSunburst
    Histogram = 118,                // xlHistogram
    Pareto = 122,                   // xlPareto
    BoxWhisker = 121,               // xlBoxWhisker
    Waterfall = 119,                // xlWaterfall
    Funnel = 123,                   // xlFunnel
    
    // === COMBO CHARTS ===
    ColumnLineCombo = 120,          // xlColumnLineCombo (approximation)
    RegionMap = 140                 // xlRegionMap (Excel 365)
}

public enum ChartAxisType
{
    Primary,
    Secondary,
    Category,
    Value
}

public enum LegendPosition
{
    Bottom = -4107,     // xlLegendPositionBottom
    Corner = 2,         // xlLegendPositionCorner
    Custom = -4161,     // xlLegendPositionCustom
    Left = -4131,       // xlLegendPositionLeft
    Right = -4152,      // xlLegendPositionRight
    Top = -4160         // xlLegendPositionTop
}

public class ChartListResult
{
    public List<ChartInfo> Charts { get; set; } = new();
    public string FilePath { get; set; } = string.Empty;
}

public class ChartInfo
{
    public string Name { get; set; } = string.Empty;
    public string SheetName { get; set; } = string.Empty;
    public ChartType ChartType { get; set; }
    public bool IsPivotChart { get; set; }
    public string? LinkedPivotTable { get; set; }
    public double Left { get; set; }
    public double Top { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
    public int SeriesCount { get; set; }
}

public class ChartInfo
{
    public string Name { get; set; } = string.Empty;
    public string SheetName { get; set; } = string.Empty;
    public ChartType ChartType { get; set; }
    public bool IsPivotChart { get; set; }
    public string? LinkedPivotTable { get; set; }
    public string? SourceRange { get; set; }
    public double Left { get; set; }
    public double Top { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
    public string? Title { get; set; }
    public bool HasLegend { get; set; }
    public List<SeriesInfo> Series { get; set; } = new();
}

public class SeriesInfo
{
    public string Name { get; set; } = string.Empty;
    public string ValuesRange { get; set; } = string.Empty;
    public string? CategoryRange { get; set; }
}

public class ChartCreateResult : OperationResult
{
    public string ChartName { get; set; } = string.Empty;
    public string SheetName { get; set; } = string.Empty;
    public ChartType ChartType { get; set; }
    public bool IsPivotChart { get; set; }
    public string? LinkedPivotTable { get; set; }
    public double Left { get; set; }
    public double Top { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
}

public class ChartSeriesResult : OperationResult
{
    public string SeriesName { get; set; } = string.Empty;
    public string ValuesRange { get; set; } = string.Empty;
    public string? CategoryRange { get; set; }
    public int SeriesIndex { get; set; }
}

Strategy Pattern Design

IChartStrategy Interface

public interface IChartStrategy
{
    /// <summary>
    /// Determines if this strategy can handle the chart
    /// </summary>
    bool CanHandle(dynamic chart);
    
    /// <summary>
    /// Gets chart information
    /// </summary>
    ChartInfo GetInfo(dynamic chart, string chartName);
    
    /// <summary>
    /// Sets the data source (Regular: range, PivotChart: error)
    /// </summary>
    OperationResult SetSourceRange(dynamic chart, string sourceRange);
    
    /// <summary>
    /// Adds a series (Regular: SeriesCollection, PivotChart: error)
    /// </summary>
    ChartSeriesResult AddSeries(dynamic chart, string seriesName, 
        string valuesRange, string? categoryRange);
    
    /// <summary>
    /// Removes a series (Regular: SeriesCollection, PivotChart: error)
    /// </summary>
    OperationResult RemoveSeries(dynamic chart, int seriesIndex);
}

RegularChartStrategy

public class RegularChartStrategy : IChartStrategy
{
    public bool CanHandle(dynamic chart)
    {
        // Regular charts: chart.PivotLayout is null or doesn't exist
        try
        {
            var pivotLayout = chart.PivotLayout;
            return pivotLayout == null;
        }
        catch
        {
            return true; // No PivotLayout = Regular chart
        }
    }
    
    public OperationResult SetSourceRange(dynamic chart, string sourceRange)
    {
        // Use chart.SetSourceData(range, plotBy)
        chart.SetSourceData(sourceRange);
        return new OperationResult { Success = true };
    }
    
    public ChartSeriesResult AddSeries(dynamic chart, string seriesName, 
        string valuesRange, string? categoryRange)
    {
        dynamic seriesCollection = chart.SeriesCollection();
        dynamic newSeries = seriesCollection.NewSeries();
        newSeries.Name = seriesName;
        newSeries.Values = valuesRange;
        if (categoryRange != null)
        {
            newSeries.XValues = categoryRange;
        }
        // ... return result
    }
}

PivotChartStrategy

public class PivotChartStrategy : IChartStrategy
{
    public bool CanHandle(dynamic chart)
    {
        // PivotCharts: chart.PivotLayout exists
        try
        {
            var pivotLayout = chart.PivotLayout;
            return pivotLayout != null;
        }
        catch
        {
            return false;
        }
    }
    
    public OperationResult SetSourceRange(dynamic chart, string sourceRange)
    {
        // PivotCharts can't change source - return helpful error
        return new OperationResult
        {
            Success = false,
            ErrorMessage = "Cannot set source range for PivotChart. " +
                          "PivotCharts automatically sync with their PivotTable data source. " +
                          "To modify data, use excel_pivottable tool to update the linked PivotTable."
        };
    }
    
    public ChartSeriesResult AddSeries(dynamic chart, string seriesName, 
        string valuesRange, string? categoryRange)
    {
        // PivotCharts auto-sync with PivotTable fields - return helpful error
        string pivotTableName = chart.PivotLayout.PivotTable.Name;
        return new ChartSeriesResult
        {
            Success = false,
            ErrorMessage = $"Cannot add series directly to PivotChart. " +
                          $"PivotCharts automatically sync with PivotTable '{pivotTableName}' fields. " +
                          $"Use excel_pivottable(action: 'add-value-field', pivotTableName: '{pivotTableName}', fieldName: '<field>') " +
                          $"to add data series."
        };
    }
}

Excel COM Implementation Details

Chart Creation Patterns

Regular Chart (Modern API - Excel 2010+)

// Using Shapes.AddChart (recommended)
dynamic shapes = worksheet.Shapes;
dynamic shape = shapes.AddChart(
    XlChartType: 51,  // xlColumnClustered
    Left: 100,
    Top: 100,
    Width: 400,
    Height: 300
);
dynamic chart = shape.Chart;

// Set data source
chart.SetSourceData(worksheet.Range["A1:D10"]);

// Optional: Name the chart
shape.Name = "SalesChart";

PivotChart Creation

// Method 1: From PivotCache
dynamic pivotTable = FindPivotTable(workbook, "SalesPivot");
dynamic pivotCache = pivotTable.PivotCache();

// CreatePivotChart returns Shape object containing the chart
dynamic pivotChartShape = pivotCache.CreatePivotChart(
    Destination: worksheet.Range["H1"]  // Top-left corner
);

// Access the chart
dynamic pivotChart = pivotChartShape.Chart;

// Set chart type
pivotChart.ChartType = 51;  // xlColumnClustered

// Position/resize
pivotChartShape.Left = 500;
pivotChartShape.Top = 100;
pivotChartShape.Width = 400;
pivotChartShape.Height = 300;

Chart Detection (Regular vs PivotChart)

public static bool IsPivotChart(dynamic chart)
{
    try
    {
        var pivotLayout = chart.PivotLayout;
        return pivotLayout != null;
    }
    catch
    {
        return false; // No PivotLayout property = Regular chart
    }
}

Finding Charts

// Find by shape name
dynamic shapes = worksheet.Shapes;
for (int i = 1; i <= shapes.Count; i++)
{
    dynamic shape = shapes.Item(i);
    if (shape.Type == 3)  // msoChart = 3
    {
        if (shape.Name == chartName)
        {
            return shape.Chart;
        }
    }
}

// OR use ChartObjects collection (legacy but still works)
dynamic chartObjects = worksheet.ChartObjects;
for (int i = 1; i <= chartObjects.Count; i++)
{
    dynamic chartObject = chartObjects.Item(i);
    if (chartObject.Name == chartName)
    {
        return chartObject.Chart;
    }
}

MCP Tool: excel_chart

Actions (20-25 operations)

{
  "name": "excel_chart",
  "description": "Excel chart operations - create and manage Regular Charts and PivotCharts",
  "parameters": {
    "action": "enum<ChartAction>",
    "excelPath": "string",
    "sessionId": "string",
    "chartName": "string",
    "sheetName": "string",
    "sourceRange": "string",
    "pivotTableName": "string",
    "chartType": "enum<ChartType>",
    "left": "double",
    "top": "double",
    "width": "double",
    "height": "double",
    "seriesName": "string",
    "valuesRange": "string",
    "categoryRange": "string",
    "seriesIndex": "int",
    "title": "string",
    "axis": "enum<ChartAxisType>",
    "visible": "boolean",
    "legendPosition": "enum<LegendPosition>",
    "styleId": "int"
  },
  "actions": [
    // Lifecycle (7 ops)
    "list",                     // List all charts
    "read",                     // Get chart details
    "create-from-range",        // Create Regular Chart
    "create-from-pivottable",   // Create PivotChart
    "delete",                   // Delete chart
    "move",                     // Move/resize chart
    
    // Data Source (3 ops)
    "set-source-range",         // Set data source (Regular only)
    "add-series",               // Add series (Regular only, PivotChart returns error)
    "remove-series",            // Remove series (Regular only, PivotChart returns error)
    
    // Appearance (5 ops)
    "set-chart-type",           // Change chart type
    "set-title",                // Set chart title
    "set-axis-title",           // Set axis title
    "show-legend",              // Show/hide legend
    "set-style"                 // Apply style (1-48)
  ]
}

CLI Commands

# === LIFECYCLE ===
excelcli chart list <session-id>
excelcli chart read <session-id> <chart-name>
excelcli chart create-from-range <session-id> <sheet> <range> <type> <left> <top> [width] [height] [name]
excelcli chart create-from-pivottable <session-id> <pivot-name> <sheet> <type> <left> <top> [width] [height] [name]
excelcli chart delete <session-id> <chart-name>
excelcli chart move <session-id> <chart-name> [left] [top] [width] [height]

# === DATA SOURCE ===
excelcli chart set-source-range <session-id> <chart-name> <range>
excelcli chart add-series <session-id> <chart-name> <series-name> <values-range> [category-range]
excelcli chart remove-series <session-id> <chart-name> <series-index>

# === APPEARANCE ===
excelcli chart set-chart-type <session-id> <chart-name> <type>
excelcli chart set-title <session-id> <chart-name> <title>
excelcli chart set-axis-title <session-id> <chart-name> <axis> <title>
excelcli chart show-legend <session-id> <chart-name> <true|false> [position]
excelcli chart set-style <session-id> <chart-name> <style-id>

Usage Examples

Creating Regular Chart

// Create column chart from range
var result = await chartCommands.CreateFromRange(
    batch, 
    sheetName: "Data",
    sourceRange: "A1:D10",
    chartType: ChartType.ColumnClustered,
    left: 100,
    top: 100,
    width: 400,
    height: 300,
    chartName: "SalesChart"
);

// Customize appearance
await chartCommands.SetTitle(batch, "SalesChart", "Sales by Region");
await chartCommands.SetAxisTitle(batch, "SalesChart", ChartAxisType.Value, "Revenue ($)");
await chartCommands.ShowLegend(batch, "SalesChart", true, LegendPosition.Bottom);

Creating PivotChart

// Create PivotChart from existing PivotTable
var result = await chartCommands.CreateFromPivotTable(
    batch,
    pivotTableName: "SalesPivot",
    sheetName: "Dashboard",
    chartType: ChartType.ColumnClustered,
    left: 500,
    top: 100
);

// PivotChart automatically syncs with PivotTable
// To add data series, use excel_pivottable tool:
await pivotCommands.AddValueField(batch, "SalesPivot", "Revenue", AggregationFunction.Sum);
// PivotChart updates automatically!

Success Criteria

Phase 1 (MVP) - 🚧 IN PROGRESS

Lifecycle Operations (6/6):

  • 🚧 List - List all charts
  • 🚧 Read - Get chart configuration
  • 🚧 CreateFromRange - Create Regular Chart
  • 🚧 CreateFromPivotTable - Create PivotChart
  • 🚧 Delete - Delete chart
  • 🚧 Move - Move/resize chart

Data Source Operations (3/3):

  • 🚧 SetSourceRange - Set range (Regular only)
  • 🚧 AddSeries - Add series (Regular only, PivotChart error)
  • 🚧 RemoveSeries - Remove series (Regular only, PivotChart error)

Appearance Operations (5/5):

  • 🚧 SetChartType - Change chart type (all 70+ types)
  • 🚧 SetTitle - Set chart title
  • 🚧 SetAxisTitle - Set axis title
  • 🚧 ShowLegend - Show/hide legend
  • 🚧 SetStyle - Apply chart style

Integration:

  • 🚧 MCP Server tool (excel_chart with ~15 actions)
  • 🚧 CLI commands (all 15 operations)
  • 🚧 Integration tests with both chart types

Future Enhancements (Phase 2)

  • Advanced formatting (colors, fonts, borders)
  • Axis scaling and formatting
  • Data labels
  • Trendlines

Implementation Timeline

Phase 1 (Core Operations): 🚧 IN PROGRESS (November 22, 2025 - December 2025)

  • Specification and interface design
  • Strategy pattern implementation
  • Core lifecycle and appearance operations
  • MCP Server and CLI integration
  • Integration tests
  • Estimated Time: 1-2 weeks

Phase 2 (Advanced Features): ⏸️ FUTURE (On demand)

  • Advanced formatting and customization
  • Chart export capabilities
  • Estimated Time: 1 week when prioritized

Open Questions

  1. Chart naming - Excel auto-generates names like "Chart 1". Should we force users to provide names or auto-generate meaningful ones?

  2. Default positioning - Should we have a smart default positioning algorithm (e.g., place charts below/beside data) or always require explicit coordinates?

  3. Chart refresh - PivotCharts auto-refresh. Should we expose a Refresh operation for Regular Charts to re-read source data?

  4. Chart export - Should chart-to-image export be Phase 1 or Phase 2?

Recommended Answers:

  1. Optional names - Auto-generate meaningful names like "Chart_Data_A1D10" if not provided
  2. Explicit coordinates - Always require positioning (LLMs can calculate, prevents unexpected placement)
  3. No Refresh for Regular - Charts update automatically when data changes. Not needed.
  4. Phase 2 - Export is advanced feature, not core automation