Skip to main content
Glama
ishayoyo

Excel MCP Server

by ishayoyo

auto_fit_columns

Automatically adjust column widths to fit content in Excel files. Specify columns, sheets, and width limits to optimize spreadsheet readability.

Instructions

Automatically adjust column widths to fit content in Excel files

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filePathYesPath to the Excel file (.xlsx or .xls)
sheetNoSheet name (optional, defaults to all sheets)
columnsNoSpecific columns to auto-fit (optional, defaults to all columns). Can be column letters (e.g., ["A", "B"]) or numbers (e.g., [1, 2])
minWidthNoMinimum column width (default: 10)
maxWidthNoMaximum column width (default: 60)
paddingNoExtra padding to add to calculated width (default: 2)

Implementation Reference

  • The core handler function that implements auto column fitting logic for Excel files. It reads the workbook, calculates maximum content length per column (accounting for font size), applies min/max widths and padding, adjusts column widths, and saves the file. Supports specific sheets or all sheets, specific columns or all.
    async autoFitColumns(args: ToolArgs): Promise<ToolResponse> {
      const { filePath, sheet, columns, minWidth = 10, maxWidth = 60, padding = 2 } = args;
      const ext = path.extname(filePath).toLowerCase();
      const absolutePath = path.resolve(filePath);
    
      if (ext !== '.xlsx' && ext !== '.xls') {
        throw new Error('Auto-fit columns only works with Excel files (.xlsx or .xls)');
      }
    
      try {
        await fs.access(absolutePath);
      } catch {
        throw new Error(`File not found: ${filePath}`);
      }
    
      // Read existing workbook
      const workbook = new ExcelJS.Workbook();
      await workbook.xlsx.readFile(absolutePath);
    
      let worksheets: ExcelJS.Worksheet[] = [];
    
      if (sheet) {
        const targetSheet = workbook.getWorksheet(sheet);
        if (!targetSheet) {
          throw new Error(`Sheet "${sheet}" not found`);
        }
        worksheets = [targetSheet];
      } else {
        // Auto-fit all worksheets if no specific sheet provided
        worksheets = workbook.worksheets;
      }
    
      const results: any[] = [];
    
      for (const worksheet of worksheets) {
        const columnInfo: any[] = [];
        const columnCount = worksheet.columnCount || 0;
    
        // Determine which columns to process
        let columnsToProcess: number[] = [];
        if (columns && Array.isArray(columns)) {
          // Convert column letters/numbers to column indices
          columnsToProcess = columns.map(col => {
            if (typeof col === 'string') {
              // Handle column letters like "A", "B", "C"
              const parsed = parseA1Notation(col + '1');
              return parsed.col + 1; // Convert to 1-based for ExcelJS
            } else if (typeof col === 'number') {
              return col; // Assume already 1-based
            }
            return 1;
          });
        } else {
          // Auto-fit all columns
          columnsToProcess = Array.from({ length: columnCount }, (_, i) => i + 1);
        }
    
        for (const colIndex of columnsToProcess) {
          if (colIndex > columnCount) continue;
    
          const column = worksheet.getColumn(colIndex);
          let maxContentLength = 0;
    
          // Iterate through all cells in the column to find max content length
          column.eachCell({ includeEmpty: false }, (cell) => {
            const cellValue = cell.value;
            let contentLength = 0;
    
            if (cellValue !== null && cellValue !== undefined) {
              if (typeof cellValue === 'string') {
                contentLength = cellValue.length;
              } else if (typeof cellValue === 'number') {
                contentLength = cellValue.toString().length;
              } else if (cellValue && typeof cellValue === 'object' && 'richText' in cellValue) {
                // Handle rich text
                contentLength = cellValue.richText?.reduce((sum: number, part: any) =>
                  sum + (part.text?.length || 0), 0) || 0;
              } else if (cellValue && typeof cellValue === 'object' && 'formula' in cellValue) {
                // For formula cells, use the result or a reasonable default
                const result = cellValue.result;
                if (result !== null && result !== undefined) {
                  contentLength = result.toString().length;
                } else {
                  contentLength = 15; // Default width for formulas
                }
              } else {
                contentLength = cellValue.toString().length;
              }
            }
    
            // Account for font size (approximate character width scaling)
            const font = cell.font;
            let fontMultiplier = 1;
            if (font && font.size) {
              fontMultiplier = font.size / 11; // 11 is default Excel font size
            }
    
            contentLength *= fontMultiplier;
            maxContentLength = Math.max(maxContentLength, contentLength);
          });
    
          // Apply padding and constraints
          let optimalWidth = maxContentLength + padding;
          optimalWidth = Math.max(optimalWidth, minWidth);
          optimalWidth = Math.min(optimalWidth, maxWidth);
    
          // Set the column width
          column.width = optimalWidth;
    
          columnInfo.push({
            columnIndex: colIndex,
            columnLetter: this.getColumnLetter(colIndex),
            maxContentLength: Math.round(maxContentLength * 100) / 100,
            finalWidth: Math.round(optimalWidth * 100) / 100,
          });
        }
    
        results.push({
          sheetName: worksheet.name,
          columnsProcessed: columnsToProcess.length,
          columnDetails: columnInfo,
        });
      }
    
      // Save the workbook
      await workbook.xlsx.writeFile(absolutePath);
    
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              success: true,
              filePath: absolutePath,
              sheetsProcessed: results.length,
              totalColumnsAdjusted: results.reduce((sum, sheet) => sum + sheet.columnsProcessed, 0),
              results,
              settings: {
                minWidth,
                maxWidth,
                padding,
              },
            }, null, 2),
          },
        ],
      };
    }
  • JSON schema defining the input parameters for the auto_fit_columns tool, including filePath (required), optional sheet, columns array (strings or numbers), and optional minWidth, maxWidth, padding with defaults.
      name: 'auto_fit_columns',
      description: 'Automatically adjust column widths to fit content in Excel files',
      inputSchema: {
        type: 'object',
        properties: {
          filePath: {
            type: 'string',
            description: 'Path to the Excel file (.xlsx or .xls)',
          },
          sheet: {
            type: 'string',
            description: 'Sheet name (optional, defaults to all sheets)',
          },
          columns: {
            type: 'array',
            description: 'Specific columns to auto-fit (optional, defaults to all columns). Can be column letters (e.g., ["A", "B"]) or numbers (e.g., [1, 2])',
            items: {
              oneOf: [
                { type: 'string', description: 'Column letter (e.g., "A", "B", "C")' },
                { type: 'number', description: 'Column number (1-based, e.g., 1, 2, 3)' }
              ]
            }
          },
          minWidth: {
            type: 'number',
            description: 'Minimum column width (default: 10)',
            default: 10
          },
          maxWidth: {
            type: 'number',
            description: 'Maximum column width (default: 60)',
            default: 60
          },
          padding: {
            type: 'number',
            description: 'Extra padding to add to calculated width (default: 2)',
            default: 2
          }
        },
        required: ['filePath'],
      },
    },
  • src/index.ts:1253-1254 (registration)
    Tool registration in the CallToolRequestHandler switch statement, dispatching calls to 'auto_fit_columns' to the FileOperationsHandler.autoFitColumns method.
    case 'auto_fit_columns':
      return await this.fileOpsHandler.autoFitColumns(toolArgs);
  • Helper method to convert 1-based column index to Excel column letter (A, B, ..., Z, AA, etc.) used in results reporting.
    private getColumnLetter(columnIndex: number): string {
      let result = '';
      let index = columnIndex - 1; // Convert to 0-based
    
      while (index >= 0) {
        result = String.fromCharCode(65 + (index % 26)) + result;
        index = Math.floor(index / 26) - 1;
      }
    
      return result;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden but only states what the tool does, not how it behaves. It doesn't disclose whether this modifies files in-place, requires write permissions, handles errors, or provides any feedback. For a tool that modifies Excel files, this is a significant gap in behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that immediately conveys the core functionality. Every word earns its place with no redundancy or unnecessary elaboration, making it perfectly front-loaded and concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool that modifies Excel files with 6 parameters and no annotations or output schema, the description is insufficient. It doesn't explain what happens after execution (e.g., file saved, success confirmation), error conditions, or behavioral constraints, leaving critical gaps for an agent to use it effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all 6 parameters. The description adds no parameter-specific information beyond what's already in the schema descriptions, maintaining the baseline score of 3 for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Automatically adjust column widths to fit content') and the target resource ('in Excel files'). It distinguishes itself from siblings like 'format_cells' or 'get_range' by focusing exclusively on column width optimization.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., file must exist), compare to manual formatting, or suggest when to use sibling tools like 'format_cells' for other formatting needs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ishayoyo/excel-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server